Skip to content

Commit

Permalink
Use GpuArrayBuffer for MeshUniform
Browse files Browse the repository at this point in the history
  • Loading branch information
superdump committed Jul 23, 2023
1 parent eb485b1 commit 574516f
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 87 deletions.
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>,
}

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());
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])
}
};
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

0 comments on commit 574516f

Please sign in to comment.