Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use GpuArrayBuffer for MeshUniform #9254

Merged
merged 15 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions assets/shaders/custom_vertex_attribute.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct CustomMaterial {
var<uniform> material: CustomMaterial;

struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,
@location(1) blend_color: vec4<f32>,
};
Expand All @@ -21,8 +22,8 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput {
var out: VertexOutput;
out.clip_position = mesh_position_local_to_clip(
mesh.model,
vec4<f32>(vertex.position, 1.0)
mesh[vertex.instance_index].model,
vec4<f32>(vertex.position, 1.0),
);
out.blend_color = vertex.blend_color;
return out;
Expand Down
6 changes: 5 additions & 1 deletion assets/shaders/instancing.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ struct VertexOutput {
fn vertex(vertex: Vertex) -> VertexOutput {
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
var out: VertexOutput;
// NOTE: The 0 index into the Mesh array is a hack for this example as the
// instance_index builtin would map to the wrong index in the Mesh array.
// This index could be passed in via another uniform instead but it's
// unnecessary for the example.
out.clip_position = mesh_position_local_to_clip(
mesh.model,
mesh[0].model,
vec4<f32>(position, 1.0)
);
out.color = vertex.i_color;
Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use bevy_render::{
BindGroupLayoutEntry, BindingResource, BindingType, BlendState, BufferBindingType,
ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
DynamicUniformBuffer, FragmentState, FrontFace, MultisampleState, PipelineCache,
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderRef, ShaderStages,
ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
PolygonMode, PrimitiveState, RenderPipelineDescriptor, Shader, ShaderDefVal, ShaderRef,
ShaderStages, ShaderType, SpecializedMeshPipeline, SpecializedMeshPipelineError,
SpecializedMeshPipelines, StencilFaceState, StencilState, TextureSampleType,
TextureViewDimension, VertexState,
},
Expand Down Expand Up @@ -226,6 +226,7 @@ pub struct PrepassPipeline<M: Material> {
pub view_layout_motion_vectors: BindGroupLayout,
pub view_layout_no_motion_vectors: BindGroupLayout,
pub mesh_layouts: MeshLayouts,
pub mesh_buffer_batch_size: Option<u32>,
pub material_layout: BindGroupLayout,
pub material_vertex_shader: Option<Handle<Shader>>,
pub material_fragment_shader: Option<Handle<Shader>>,
Expand Down Expand Up @@ -313,6 +314,7 @@ impl<M: Material> FromWorld for PrepassPipeline<M> {
view_layout_motion_vectors,
view_layout_no_motion_vectors,
mesh_layouts: mesh_pipeline.mesh_layouts.clone(),
mesh_buffer_batch_size: mesh_pipeline.mesh_buffer_batch_size,
material_vertex_shader: match M::prepass_vertex_shader() {
ShaderRef::Default => None,
ShaderRef::Handle(handle) => Some(handle),
Expand Down Expand Up @@ -352,6 +354,10 @@ where
let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new();

if let Some(batch_size) = self.mesh_buffer_batch_size {
shader_defs.push(ShaderDefVal::UInt("MESH_BATCH_SIZE".into(), batch_size));
}

// NOTE: Eventually, it would be nice to only add this when the shaders are overloaded by the Material.
// The main limitation right now is that bind group order is hardcoded in shaders.
bind_group_layouts.insert(1, self.material_layout.clone());
Expand Down
16 changes: 12 additions & 4 deletions crates/bevy_pbr/src/prepass/prepass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Most of these attributes are not used in the default prepass fragment shader, but they are still needed so we can
// pass them to custom prepass shaders like pbr_prepass.wgsl.
struct Vertex {
@builtin(instance_index) instance_index: u32,
@location(0) position: vec3<f32>,

#ifdef VERTEX_UVS
Expand Down Expand Up @@ -88,7 +89,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef SKINNED
var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else // SKINNED
var model = mesh.model;
var model = mesh[vertex.instance_index].model;
#endif // SKINNED

out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0));
Expand All @@ -105,17 +106,24 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput {
#ifdef SKINNED
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
#else // SKINNED
out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(vertex.normal);
out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world(
vertex.normal,
vertex.instance_index
);
#endif // SKINNED

#ifdef VERTEX_TANGENTS
out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world(
model,
vertex.tangent,
vertex.instance_index
);
#endif // VERTEX_TANGENTS
#endif // NORMAL_PREPASS

#ifdef MOTION_VECTOR_PREPASS
out.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(model, vec4<f32>(vertex.position, 1.0));
out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(mesh.previous_model, vec4<f32>(vertex.position, 1.0));
out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(mesh[vertex.instance_index].previous_model, vec4<f32>(vertex.position, 1.0));
#endif // MOTION_VECTOR_PREPASS

return out;
Expand Down
3 changes: 1 addition & 2 deletions crates/bevy_pbr/src/prepass/prepass_bindings.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ var<uniform> previous_view_proj: mat4x4<f32>;

// Material bindings will be in @group(1)

@group(2) @binding(0)
var<uniform> mesh: bevy_pbr::mesh_types::Mesh;
#import bevy_pbr::mesh_bindings mesh
59 changes: 38 additions & 21 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use bevy_ecs::{
use bevy_math::{Mat3A, Mat4, Vec2};
use bevy_reflect::TypeUuid;
use bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
globals::{GlobalsBuffer, GlobalsUniform},
gpu_component_array_buffer::GpuComponentArrayBufferPlugin,
mesh::{
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
GpuBufferInfo, InnerMeshVertexBufferLayout, Mesh, MeshVertexBufferLayout,
Expand Down Expand Up @@ -121,7 +121,7 @@ impl Plugin for MeshRenderPlugin {
load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl);
load_internal_asset!(app, MORPH_HANDLE, "morph.wgsl", Shader::from_wgsl);

app.add_plugins(UniformComponentPlugin::<MeshUniform>::default());
app.add_plugins(GpuComponentArrayBufferPlugin::<MeshUniform>::default());

if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
Expand Down Expand Up @@ -309,8 +309,8 @@ pub struct MeshPipeline {
// This dummy white texture is to be used in place of optional StandardMaterial textures
pub dummy_white_gpu_image: GpuImage,
pub clustered_forward_buffer_binding_type: BufferBindingType,

pub mesh_layouts: MeshLayouts,
pub mesh_buffer_batch_size: Option<u32>,
superdump marked this conversation as resolved.
Show resolved Hide resolved
}

impl FromWorld for MeshPipeline {
Expand Down Expand Up @@ -550,6 +550,9 @@ impl FromWorld for MeshPipeline {
clustered_forward_buffer_binding_type,
dummy_white_gpu_image,
mesh_layouts: MeshLayouts::new(&render_device),
mesh_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(
render_device.as_ref(),
),
}
}
}
Expand Down Expand Up @@ -709,6 +712,11 @@ impl SpecializedMeshPipeline for MeshPipeline {
let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new();

shader_defs.push("INSTANCE_INDEX".into());
superdump marked this conversation as resolved.
Show resolved Hide resolved
if let Some(batch_size) = self.mesh_buffer_batch_size {
shader_defs.push(ShaderDefVal::UInt("MESH_BATCH_SIZE".into(), batch_size));
}

if layout.contains(Mesh::ATTRIBUTE_POSITION) {
shader_defs.push("VERTEX_POSITIONS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
Expand Down Expand Up @@ -932,29 +940,29 @@ pub fn queue_mesh_bind_group(
mut groups: ResMut<MeshBindGroups>,
mesh_pipeline: Res<MeshPipeline>,
render_device: Res<RenderDevice>,
mesh_uniforms: Res<ComponentUniforms<MeshUniform>>,
mesh_uniforms: Res<GpuArrayBuffer<MeshUniform>>,
skinned_mesh_uniform: Res<SkinnedMeshUniform>,
weights_uniform: Res<MorphUniform>,
) {
groups.reset();
let layouts = &mesh_pipeline.mesh_layouts;
let Some(model) = mesh_uniforms.buffer() else {
let Some(model) = mesh_uniforms.binding() else {
return;
};
groups.model_only = Some(layouts.model_only(&render_device, model));
groups.model_only = Some(layouts.model_only(&render_device, &model));

let skin = skinned_mesh_uniform.buffer.buffer();
if let Some(skin) = skin {
groups.skinned = Some(layouts.skinned(&render_device, model, skin));
groups.skinned = Some(layouts.skinned(&render_device, &model, skin));
}

if let Some(weights) = weights_uniform.buffer.buffer() {
for (id, gpu_mesh) in meshes.iter() {
if let Some(targets) = gpu_mesh.morph_targets.as_ref() {
let group = if let Some(skin) = skin.filter(|_| is_skinned(&gpu_mesh.layout)) {
layouts.morphed_skinned(&render_device, model, skin, weights, targets)
layouts.morphed_skinned(&render_device, &model, skin, weights, targets)
} else {
layouts.morphed(&render_device, model, weights, targets)
layouts.morphed(&render_device, &model, weights, targets)
};
groups.morph_targets.insert(id.id(), group);
}
Expand Down Expand Up @@ -1198,7 +1206,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
type ViewWorldQuery = ();
type ItemWorldQuery = (
Read<Handle<Mesh>>,
Read<DynamicUniformIndex<MeshUniform>>,
Read<GpuArrayBufferIndex<MeshUniform>>,
Option<Read<SkinnedMeshJoints>>,
Option<Read<MorphIndex>>,
);
Expand All @@ -1207,7 +1215,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
fn render<'w>(
_item: &P,
_view: (),
(mesh, mesh_index, skin_index, morph_index): ROQueryItem<Self::ItemWorldQuery>,
(mesh, batch_indices, skin_index, morph_index): ROQueryItem<Self::ItemWorldQuery>,
bind_groups: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
Expand All @@ -1223,13 +1231,19 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
);
return RenderCommandResult::Failure;
};

let mut set_bind_group = |indices: &[u32]| pass.set_bind_group(I, bind_group, indices);
let mesh_index = mesh_index.index();
match (skin_index, morph_index) {
(None, None) => set_bind_group(&[mesh_index]),
(Some(skin), None) => set_bind_group(&[mesh_index, skin.index]),
(None, Some(morph)) => set_bind_group(&[mesh_index, morph.index]),
(Some(skin), Some(morph)) => set_bind_group(&[mesh_index, skin.index, morph.index]),
match (batch_indices.dynamic_offset, skin_index, morph_index) {
(None, None, None) => set_bind_group(&[]),
(None, Some(skin), None) => set_bind_group(&[skin.index]),
(None, None, Some(morph)) => set_bind_group(&[morph.index]),
(None, Some(skin), Some(morph)) => set_bind_group(&[skin.index, morph.index]),
(Some(mesh_index), None, None) => set_bind_group(&[mesh_index]),
(Some(mesh_index), Some(skin), None) => set_bind_group(&[mesh_index, skin.index]),
(Some(mesh_index), None, Some(morph)) => set_bind_group(&[mesh_index, morph.index]),
(Some(mesh_index), Some(skin), Some(morph)) => {
set_bind_group(&[mesh_index, skin.index, morph.index]);
}
superdump marked this conversation as resolved.
Show resolved Hide resolved
};
RenderCommandResult::Success
}
Expand All @@ -1239,12 +1253,12 @@ pub struct DrawMesh;
impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
type Param = SRes<RenderAssets<Mesh>>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<Handle<Mesh>>;
type ItemWorldQuery = (Read<GpuArrayBufferIndex<MeshUniform>>, Read<Handle<Mesh>>);
#[inline]
fn render<'w>(
_item: &P,
_view: (),
mesh_handle: ROQueryItem<'_, Self::ItemWorldQuery>,
(batch_indices, mesh_handle): ROQueryItem<'_, Self::ItemWorldQuery>,
meshes: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
Expand All @@ -1257,10 +1271,13 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
count,
} => {
pass.set_index_buffer(buffer.slice(..), 0, *index_format);
pass.draw_indexed(0..*count, 0, 0..1);
pass.draw_indexed(0..*count, 0, batch_indices.index..batch_indices.index + 1);
}
GpuBufferInfo::NonIndexed => {
pass.draw(0..gpu_mesh.vertex_count, 0..1);
pass.draw(
0..gpu_mesh.vertex_count,
batch_indices.index..batch_indices.index + 1,
);
}
}
RenderCommandResult::Success
Expand Down
18 changes: 15 additions & 3 deletions crates/bevy_pbr/src/render/mesh.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#import bevy_pbr::mesh_vertex_output MeshVertexOutput

struct Vertex {
@builtin(instance_index) instance_index: u32,
#ifdef VERTEX_POSITIONS
@location(0) position: vec3<f32>,
#endif
Expand Down Expand Up @@ -63,14 +64,17 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput {
#ifdef SKINNED
var model = bevy_pbr::skinning::skin_model(vertex.joint_indices, vertex.joint_weights);
#else
var model = mesh.model;
var model = mesh[vertex.instance_index].model;
#endif

#ifdef VERTEX_NORMALS
#ifdef SKINNED
out.world_normal = bevy_pbr::skinning::skin_normals(model, vertex.normal);
#else
out.world_normal = mesh_functions::mesh_normal_local_to_world(vertex.normal);
out.world_normal = mesh_functions::mesh_normal_local_to_world(
vertex.normal,
vertex.instance_index
);
#endif
#endif

Expand All @@ -84,13 +88,21 @@ fn vertex(vertex_no_morph: Vertex) -> MeshVertexOutput {
#endif

#ifdef VERTEX_TANGENTS
out.world_tangent = mesh_functions::mesh_tangent_local_to_world(model, vertex.tangent);
out.world_tangent = mesh_functions::mesh_tangent_local_to_world(
model,
vertex.tangent,
vertex.instance_index
);
#endif

#ifdef VERTEX_COLORS
out.color = vertex.color;
#endif

#ifdef INSTANCE_INDEX
out.instance_index = vertex.instance_index;
#endif

return out;
}

Expand Down
Loading
Loading