From 56e802f027d755a7f2f0437ef5ce07c7c9f024ca Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Fri, 22 Sep 2023 21:40:06 +0200 Subject: [PATCH] Revert to more sensible mesh_bindings.rs --- crates/bevy_core_pipeline/src/taa/mod.rs | 2 - crates/bevy_pbr/src/prepass/prepass.wgsl | 22 +- crates/bevy_pbr/src/render/double_buffer.rs | 30 +- crates/bevy_pbr/src/render/mesh.rs | 76 +-- crates/bevy_pbr/src/render/mesh_bindings.rs | 613 +++++++++----------- examples/shader/shader_instancing.rs | 2 +- 6 files changed, 333 insertions(+), 412 deletions(-) diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index e61dccc5d4bf5c..f082a785eb0091 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -132,8 +132,6 @@ pub struct TemporalAntiAliasBundle { /// /// Cannot be used with [`bevy_render::camera::OrthographicProjection`]. /// -/// Currently does not support skinned meshes and morph targets. -/// There will probably be ghosting artifacts if used with them. /// Does not work well with alpha-blended meshes as it requires depth writing to determine motion. /// /// It is very important that correct motion vectors are written for everything on screen. diff --git a/crates/bevy_pbr/src/prepass/prepass.wgsl b/crates/bevy_pbr/src/prepass/prepass.wgsl index 7f169ed0f24368..8b4161d0d218ef 100644 --- a/crates/bevy_pbr/src/prepass/prepass.wgsl +++ b/crates/bevy_pbr/src/prepass/prepass.wgsl @@ -81,7 +81,7 @@ fn morph_vertex(vertex_in: Vertex) -> Vertex { fn morph_previous_position(vertex: Vertex) -> vec3 { var vertex_position = vertex.position; let weight_count = bevy_pbr::morph::layer_count(); - for (var i: u32 = 0u, i < weight_count; i++) { + for (var i: u32 = 0u; i < weight_count; i++) { let weight = bevy_pbr::morph::previous_weight_at(i); if weight != 0.0 { vertex_position += weight * bevy_pbr::morph::morph(vertex.index, bevy_pbr::morph::position_offset, i); @@ -96,6 +96,10 @@ fn morph_previous_position(vertex: Vertex) -> vec3 { fn vertex(vertex_no_morph: Vertex) -> VertexOutput { var out: VertexOutput; + // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. + // See https://github.com/gfx-rs/naga/issues/2416 + let vertex_instance = vertex_no_morph.instance_index; + #ifdef MORPH_TARGETS var vertex = morph_vertex(vertex_no_morph); #else @@ -105,9 +109,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 - // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. - // See https://github.com/gfx-rs/naga/issues/2416 - var model = bevy_pbr::mesh_functions::get_model_matrix(vertex_no_morph.instance_index); + var model = bevy_pbr::mesh_functions::get_model_matrix(vertex_instance); #endif // SKINNED out.clip_position = bevy_pbr::mesh_functions::mesh_position_local_to_clip(model, vec4(vertex.position, 1.0)); @@ -126,9 +128,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #else // SKINNED out.world_normal = bevy_pbr::mesh_functions::mesh_normal_local_to_world( vertex.normal, - // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. - // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_instance) ); #endif // SKINNED @@ -136,9 +136,7 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { out.world_tangent = bevy_pbr::mesh_functions::mesh_tangent_local_to_world( model, vertex.tangent, - // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. - // See https://github.com/gfx-rs/naga/issues/2416 - get_instance_index(vertex_no_morph.instance_index) + get_instance_index(vertex_instance) ); #endif // VERTEX_TANGENTS #endif // NORMAL_PREPASS @@ -152,11 +150,9 @@ fn vertex(vertex_no_morph: Vertex) -> VertexOutput { #ifdef SKINNED var previous_model = bevy_pbr::skinning::previous_skin_model(vertex.joint_indices, vertex.joint_weights); #else - var previous_model = bevy_pbr::mesh_functions::get_previous_model_matrix(instance_index); + var previous_model = bevy_pbr::mesh_functions::get_previous_model_matrix(vertex_instance); #endif out.world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world(model, vec4(vertex.position, 1.0)); - // Use vertex_no_morph.instance_index instead of vertex.instance_index to work around a wgpu dx12 bug. - // See https://github.com/gfx-rs/naga/issues/2416 out.previous_world_position = bevy_pbr::mesh_functions::mesh_position_local_to_world( previous_model, vec4(previous_position, 1.0) diff --git a/crates/bevy_pbr/src/render/double_buffer.rs b/crates/bevy_pbr/src/render/double_buffer.rs index 3392e45a100dec..bb4499faca4d1c 100644 --- a/crates/bevy_pbr/src/render/double_buffer.rs +++ b/crates/bevy_pbr/src/render/double_buffer.rs @@ -5,17 +5,20 @@ use bevy_render::{ use bytemuck::Pod; #[derive(Clone, Copy, PartialEq, Eq)] -pub enum Side { +enum Side { A, B, } impl Side { - fn toggle(&mut self) { - *self = match self { + fn flip(self) -> Self { + match self { Side::A => Side::B, Side::B => Side::A, } } + fn toggle(&mut self) { + *self = self.flip(); + } } pub struct DoubleBufferVec { a: BufferVec, @@ -36,8 +39,8 @@ impl DoubleBufferVec { current: Side::A, } } - pub(crate) const fn current_buffer(&self) -> &BufferVec { - match self.current { + const fn buffer_side(&self, side: Side) -> &BufferVec { + match side { Side::A => &self.a, Side::B => &self.b, } @@ -49,22 +52,19 @@ impl DoubleBufferVec { } } pub fn current(&self) -> Option<&Buffer> { - self.current_buffer().buffer() + self.buffer_side(self.current).buffer() } pub fn previous(&self) -> Option<&Buffer> { - let old_buffer = match self.current { - Side::A => &self.b, - Side::B => &self.a, - }; - old_buffer - .buffer() - .or_else(|| self.current_buffer().buffer()) + let previous_buffer = self.buffer_side(self.current.flip()); + // Workaround a panic in first frame of morph target init. + let current = || self.current(); + previous_buffer.buffer().or_else(current) } pub fn is_empty(&self) -> bool { - self.current_buffer().is_empty() + self.buffer_side(self.current).is_empty() } pub fn len(&self) -> usize { - self.current_buffer().len() + self.buffer_side(self.current).len() } pub fn clear(&mut self) { self.current.toggle(); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index c8accf195378d4..823c2a2696785a 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -47,6 +47,7 @@ use bevy_transform::components::GlobalTransform; use bevy_utils::{tracing::error, EntityHashMap, Hashed}; use crate::render::{ + mesh_bindings::MeshBindGroups, morph::{ extract_morphs, no_automatic_morph_batching, prepare_morphs, MorphIndices, MorphUniform, }, @@ -54,8 +55,6 @@ use crate::render::{ MeshLayouts, }; -use super::mesh_bindings::MeshBindGroups; - #[derive(Default)] pub struct MeshRenderPlugin; @@ -716,24 +715,19 @@ pub fn setup_morph_and_skinning_defs( shader_defs: &mut Vec, vertex_attributes: &mut Vec, ) -> BindGroupLayout { - let mut add_skin_data = || { + let mvectors = key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS); + let morph = key.contains(MeshPipelineKey::MORPH_TARGETS); + let skin = is_skinned(layout); + + if skin { shader_defs.push("SKINNED".into()); vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_INDEX.at_shader_location(offset)); vertex_attributes.push(Mesh::ATTRIBUTE_JOINT_WEIGHT.at_shader_location(offset + 1)); - }; - let is_mvectors = key.contains(MeshPipelineKey::MOTION_VECTOR_PREPASS); - let is_morphed = key.contains(MeshPipelineKey::MORPH_TARGETS); - let is_skinned = is_skinned(layout); - match (is_skinned, is_morphed) { - (true, false) => add_skin_data(), - (false, true) => shader_defs.push("MORPH_TARGETS".into()), - (false, false) => {} - (true, true) => { - add_skin_data(); - shader_defs.push("MORPH_TARGETS".into()); - } - }; - mesh_layouts.get(is_morphed, is_skinned, is_mvectors) + } + if morph { + shader_defs.push("MORPH_TARGETS".into()); + } + mesh_layouts.get(morph, skin, mvectors).clone() } impl SpecializedMeshPipeline for MeshPipeline { @@ -964,54 +958,42 @@ impl SpecializedMeshPipeline for MeshPipeline { pub fn prepare_mesh_bind_group( meshes: Res>, - groups: ResMut, + mut groups: ResMut, mesh_pipeline: Res, render_device: Res, mesh_uniforms: Res>, skins_uniform: Res, weights_uniform: Res, - has_mvectors: Query<(), With>, ) { - let has_mvectors = has_mvectors.iter().len() != 0; - let has_mvectors = |_: &_| has_mvectors; - - let device = render_device.into_inner(); - let groups = groups.into_inner(); - let layouts = &mesh_pipeline.mesh_layouts; - let Some(model) = mesh_uniforms.binding() else { return; }; - let mut bind_groups = layouts.updater(groups, device, model); + let layouts = &mesh_pipeline.mesh_layouts; + let mut bind_groups = layouts.bind_group_maker(&render_device, model, &mut groups); + + // no morph + maybe(skin) * mvectors + no mvectors let skin = skins_uniform.buffer.current(); - let previous_skin = skins_uniform.buffer.previous().filter(has_mvectors); + let previous_skin = skins_uniform.buffer.previous(); + bind_groups.add(None, None, None, None); + bind_groups.add(None, skin, None, None); + bind_groups.add(None, skin, None, previous_skin); - let skin = skins_uniform.buffer.buffer(); - bind_groups.insert(skin, None, previous_skin, None); - if previous_skin.is_some() { - bind_groups.insert(skin, None, None, None); - } - if skin.is_some() { - bind_groups.insert(None, None, None, None); - } let Some(weights) = weights_uniform.buffer.current() else { return; }; for (id, gpu_mesh) in meshes.iter() { + let previous_weights = weights_uniform.buffer.previous(); + + let skin = skin.filter(|_| is_skinned(&gpu_mesh.layout)); + let previous_skin = previous_skin.filter(|_| is_skinned(&gpu_mesh.layout)); + let Some(targets) = gpu_mesh.morph_targets.as_ref() else { continue; }; - let is_skinned = |_: &_| is_skinned(&gpu_mesh.layout); - let skin = skin.filter(is_skinned); - let previous_skin = skin.filter(is_skinned); - let previous_weights = weights_uniform.buffer.previous().filter(has_mvectors); - let weights = Some((id, weights, targets)); - - bind_groups.insert(skin, weights, previous_skin, previous_weights); - - if previous_weights.is_some() { - bind_groups.insert(skin, weights, previous_skin, None); - } + // morph + maybe(skin) * mvectors + no mvectors + let morph = Some((id, weights, targets)); + bind_groups.add(morph, skin, previous_weights, previous_skin); + bind_groups.add(morph, skin, None, previous_skin); } } diff --git a/crates/bevy_pbr/src/render/mesh_bindings.rs b/crates/bevy_pbr/src/render/mesh_bindings.rs index 5d986e1d7009ba..976fe98a7595ec 100644 --- a/crates/bevy_pbr/src/render/mesh_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_bindings.rs @@ -1,20 +1,15 @@ //! Bind group layout related definitions for the mesh pipeline. use bevy_asset::AssetId; -use bevy_ecs::system::Resource; +use bevy_ecs::prelude::Resource; use bevy_math::Mat4; -use bevy_render::{ - mesh::morph::MAX_MORPH_WEIGHTS, - prelude::Mesh, - render_resource::{ - BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BindingResource, Buffer, TextureView, - }, - renderer::RenderDevice, -}; -use bevy_utils::{all_tuples, HashMap}; +use bevy_render::mesh::{morph::MAX_MORPH_WEIGHTS, Mesh}; +use bevy_render::{render_resource::*, renderer::RenderDevice}; +use bevy_utils::HashMap; +use smallvec::SmallVec; use crate::render::skin::MAX_JOINTS; +use crate::MeshUniform; const MORPH_WEIGHT_SIZE: usize = std::mem::size_of::(); pub const MORPH_BUFFER_SIZE: usize = MAX_MORPH_WEIGHTS * MORPH_WEIGHT_SIZE; @@ -22,382 +17,332 @@ pub const MORPH_BUFFER_SIZE: usize = MAX_MORPH_WEIGHTS * MORPH_WEIGHT_SIZE; const JOINT_SIZE: usize = std::mem::size_of::(); pub(crate) const JOINT_BUFFER_SIZE: usize = MAX_JOINTS * JOINT_SIZE; -/// Individual layout entries. -mod layout_entry { - use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE}; - use crate::MeshUniform; - use bevy_render::{ - render_resource::{ - BindGroupLayoutEntry, BindingType, BufferBindingType, BufferSize, GpuArrayBuffer, - ShaderStages, TextureSampleType, TextureViewDimension, - }, - renderer::RenderDevice, - }; +// --- Individual layout entries --- - fn buffer(binding: u32, size: u64, visibility: ShaderStages) -> BindGroupLayoutEntry { - BindGroupLayoutEntry { - binding, - visibility, - count: None, - ty: BindingType::Buffer { - ty: BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: BufferSize::new(size), - }, - } - } - pub(super) fn model(render_device: &RenderDevice, binding: u32) -> BindGroupLayoutEntry { - GpuArrayBuffer::::binding_layout( - binding, - ShaderStages::VERTEX_FRAGMENT, - render_device, - ) +fn buffer_layout(binding: u32, size: u64, visibility: ShaderStages) -> BindGroupLayoutEntry { + BindGroupLayoutEntry { + binding, + visibility, + count: None, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: BufferSize::new(size), + }, } - pub(super) fn skinning(binding: u32) -> BindGroupLayoutEntry { - buffer(binding, JOINT_BUFFER_SIZE as u64, ShaderStages::VERTEX) +} +fn model_layout(render_device: &RenderDevice, binding: u32) -> BindGroupLayoutEntry { + GpuArrayBuffer::::binding_layout( + binding, + ShaderStages::VERTEX_FRAGMENT, + render_device, + ) +} +fn skinning_layout(binding: u32) -> BindGroupLayoutEntry { + buffer_layout(binding, JOINT_BUFFER_SIZE as u64, ShaderStages::VERTEX) +} +fn weights_layout(binding: u32) -> BindGroupLayoutEntry { + buffer_layout(binding, MORPH_BUFFER_SIZE as u64, ShaderStages::VERTEX) +} +fn targets_layout(binding: u32) -> BindGroupLayoutEntry { + BindGroupLayoutEntry { + binding, + visibility: ShaderStages::VERTEX, + ty: BindingType::Texture { + view_dimension: TextureViewDimension::D3, + sample_type: TextureSampleType::Float { filterable: false }, + multisampled: false, + }, + count: None, } - pub(super) fn weights(binding: u32) -> BindGroupLayoutEntry { - buffer(binding, MORPH_BUFFER_SIZE as u64, ShaderStages::VERTEX) +} + +// --- Individual bind group entries --- + +fn entry(binding: u32, size: u64, buffer: &Buffer) -> BindGroupEntry { + BindGroupEntry { + binding, + resource: BindingResource::Buffer(BufferBinding { + buffer, + offset: 0, + size: Some(BufferSize::new(size).unwrap()), + }), } - pub(super) fn targets(binding: u32) -> BindGroupLayoutEntry { - BindGroupLayoutEntry { - binding, - visibility: ShaderStages::VERTEX, - ty: BindingType::Texture { - view_dimension: TextureViewDimension::D3, - sample_type: TextureSampleType::Float { filterable: false }, - multisampled: false, - }, - count: None, - } +} +fn model_entry(binding: u32, resource: BindingResource) -> BindGroupEntry { + BindGroupEntry { binding, resource } +} +fn skinning_entry(binding: u32, buffer: &Buffer) -> BindGroupEntry { + entry(binding, JOINT_BUFFER_SIZE as u64, buffer) +} +fn weights_entry(binding: u32, buffer: &Buffer) -> BindGroupEntry { + entry(binding, MORPH_BUFFER_SIZE as u64, buffer) +} +fn targets_entry(binding: u32, texture: &TextureView) -> BindGroupEntry { + BindGroupEntry { + binding, + resource: BindingResource::TextureView(texture), } } -/// Individual [`BindGroupEntry`](bevy_render::render_resource::BindGroupEntry) -/// for bind groups. -mod entry { - use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE}; - use bevy_render::render_resource::{ - BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferSize, TextureView, - }; - fn entry(binding: u32, size: u64, buffer: &Buffer) -> BindGroupEntry { - BindGroupEntry { - binding, - resource: BindingResource::Buffer(BufferBinding { - buffer, - offset: 0, - size: Some(BufferSize::new(size).unwrap()), - }), +// --- Layout entry lists & bind group entry lists --- + +const MVECTORS_OFFSET: u32 = 3; + +fn model_layout_entries(device: &RenderDevice) -> [BindGroupLayoutEntry; 1] { + [model_layout(device, 0)] +} +fn model_entries(model: BindingResource) -> [BindGroupEntry; 1] { + [model_entry(0, model)] +} + +fn morph_layout_entries() -> [BindGroupLayoutEntry; 2] { + [weights_layout(2), targets_layout(3)] +} +fn morph_entries<'a>(weights: &'a Buffer, targets: &'a TextureView) -> [BindGroupEntry<'a>; 2] { + [weights_entry(2, weights), targets_entry(3, targets)] +} + +fn skin_layout_entries() -> [BindGroupLayoutEntry; 1] { + [skinning_layout(1)] +} +fn skin_entries(skin: &Buffer) -> [BindGroupEntry; 1] { + [skinning_entry(1, skin)] +} + +fn mvectors_morph_layout_entries() -> [BindGroupLayoutEntry; 1] { + [weights_layout(2 + MVECTORS_OFFSET)] +} +fn mvectors_morph_entries(weights: &Buffer) -> [BindGroupEntry; 1] { + [weights_entry(2 + MVECTORS_OFFSET, weights)] +} + +fn mvectors_skin_layout_entries() -> [BindGroupLayoutEntry; 1] { + [skinning_layout(1 + MVECTORS_OFFSET)] +} +fn mvectors_skin_entries(skin: &Buffer) -> [BindGroupEntry; 1] { + [skinning_entry(1 + MVECTORS_OFFSET, skin)] +} + +/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`). +#[derive(Clone)] +pub struct MeshBindings { + pub model_only: T, + pub morphed: T, + pub skinned: T, + pub morphed_skinned: T, + pub morphed_mvectors: T, + pub skinned_mvectors: T, + pub morphed_skinned_mvectors: T, +} +impl MeshBindings { + fn get_binding(&self, binding: Binding) -> &T { + match (binding.morph, binding.skin, binding.mvectors) { + (false, false, _) => &self.model_only, + (true, false, false) => &self.morphed, + (false, true, false) => &self.skinned, + (true, true, false) => &self.morphed_skinned, + (true, false, true) => &self.morphed_mvectors, + (false, true, true) => &self.skinned_mvectors, + (true, true, true) => &self.morphed_skinned_mvectors, } } - pub(super) fn model(binding: u32, resource: BindingResource) -> BindGroupEntry { - BindGroupEntry { binding, resource } - } - pub(super) fn skinning(binding: u32, buffer: &Buffer) -> BindGroupEntry { - entry(binding, JOINT_BUFFER_SIZE as u64, buffer) +} + +#[derive(Default, Resource)] +pub struct MeshBindGroups { + pub model_only: Option, + pub morphed: HashMap, BindGroup>, + pub skinned: Option, + pub morphed_mvectors: HashMap, BindGroup>, + pub skinned_mvectors: Option, +} + +impl MeshBindGroups { + pub fn new() -> Self { + MeshBindGroups::default() } - pub(super) fn weights(binding: u32, buffer: &Buffer) -> BindGroupEntry { - entry(binding, MORPH_BUFFER_SIZE as u64, buffer) + pub fn get( + &self, + morph_id: Option>, + skin: bool, + mvectors: bool, + ) -> Option<&BindGroup> { + match (morph_id, skin, mvectors) { + (Some(id), _, true) => self.morphed_mvectors.get(&id), + (Some(id), _, false) => self.morphed.get(&id), + (None, false, _) => self.model_only.as_ref(), + (None, true, true) => self.skinned_mvectors.as_ref(), + (None, true, false) => self.skinned.as_ref(), + } } - pub(super) fn targets(binding: u32, texture: &TextureView) -> BindGroupEntry { - BindGroupEntry { - binding, - resource: BindingResource::TextureView(texture), + fn insert( + &mut self, + morph_id: Option>, + skin: bool, + mvectors: bool, + bind_group: BindGroup, + ) { + match (morph_id, skin, mvectors) { + (Some(id), _, true) => { + self.morphed_mvectors.insert(id, bind_group); + } + (Some(id), _, false) => { + self.morphed.insert(id, bind_group); + } + (None, false, _) => self.model_only = Some(bind_group), + (None, true, true) => self.skinned_mvectors = Some(bind_group), + (None, true, false) => self.skinned = Some(bind_group), } } + pub fn clear(&mut self) { + self.model_only = None; + self.morphed.clear(); + self.morphed_mvectors.clear(); + self.skinned = None; + self.skinned_mvectors = None; + } } -/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`). -#[derive(Clone)] -pub struct MeshLayouts { - layouts: [BindGroupLayout; 7], -} +pub type MeshLayouts = MeshBindings; impl MeshLayouts { /// Prepare the layouts used by the default bevy [`Mesh`]. /// /// [`Mesh`]: bevy_render::prelude::Mesh pub fn new(device: &RenderDevice) -> Self { - let layouts = COMBINATIONS.map(|bindings| { - let entries = bindings.layout_entries(device); - let label = format!("{}_layout", bindings.label()); - device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &entries, - label: Some(&label), - }) - }); - Self { layouts } + MeshLayouts { + model_only: Binding::new(false, false, false).layout(device), + morphed: Binding::new(true, false, false).layout(device), + skinned: Binding::new(false, true, false).layout(device), + morphed_skinned: Binding::new(true, true, false).layout(device), + morphed_mvectors: Binding::new(true, false, true).layout(device), + skinned_mvectors: Binding::new(false, true, true).layout(device), + morphed_skinned_mvectors: Binding::new(true, true, true).layout(device), + } } - pub fn get(&self, is_morph: bool, is_skin: bool, is_mvectors: bool) -> BindGroupLayout { - let index = combination(is_morph, is_skin, is_mvectors); - self.layouts[index].clone() + pub fn get(&self, morph: bool, skin: bool, mvectors: bool) -> &BindGroupLayout { + self.get_binding(Binding::new(morph, skin, mvectors)) } - pub fn updater<'a>( + /// Clears `bind_groups` and returns a [`MeshBindGroupMaker`], used to add + /// bind groups to `bind_groups`. + pub fn bind_group_maker<'a>( &'a self, - bind_groups: &'a mut MeshBindGroups, device: &'a RenderDevice, model: BindingResource<'a>, - ) -> MeshBindingsUpdater<'a> { + bind_groups: &'a mut MeshBindGroups, + ) -> MeshBindGroupMaker<'a> { bind_groups.clear(); - - MeshBindingsUpdater { - bind_groups, + MeshBindGroupMaker { layouts: self, device, model, + bind_groups, } } } -trait BuildCombine { - type Resource<'a>; - - fn label(&self) -> String; - fn layout_entries(&self, device: &RenderDevice) -> Box<[BindGroupLayoutEntry]>; - fn entries<'a>(&self, res: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]>; +#[derive(Default, Debug, Clone, Copy)] +pub struct Binding { + pub morph: bool, + pub skin: bool, + pub mvectors: bool, } -struct Model; -struct Morph; -struct MvMorph; -struct Skin; -struct Mv(T); - -impl BuildCombine for Option { - type Resource<'a> = Option>; - - fn label(&self) -> String { - self.as_ref().map_or_else(String::new, |t| t.label()) - } - fn layout_entries(&self, device: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - self.as_ref() - .map_or_else(Box::default, |t| t.layout_entries(device)) - } - fn entries<'a>(&self, res: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - if let Some(res) = res { - self.as_ref().map_or_else(Box::default, |t| t.entries(res)) - } else { - Box::default() +impl Binding { + pub fn new(morph: bool, skin: bool, mvectors: bool) -> Self { + Self { + morph, + skin, + mvectors, } } -} - -const MV_OFFSET: u32 = 3; - -impl BuildCombine for Mv { - type Resource<'a> = T::Resource<'a>; - - fn label(&self) -> String { - format!("mvectors_{}", self.0.label()) - } - fn layout_entries(&self, device: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - let mut entries = self.0.layout_entries(device); - entries.iter_mut().for_each(|e| { - e.binding += MV_OFFSET; - }); - entries - } - fn entries<'a>(&self, res: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - let mut entries = self.0.entries(res); - entries.iter_mut().for_each(|e| { - e.binding += MV_OFFSET; - }); - entries - } -} -impl BuildCombine for Model { - type Resource<'a> = BindingResource<'a>; - - fn label(&self) -> String { - "mesh".to_owned() - } - fn layout_entries(&self, device: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - Box::new([layout_entry::model(device, 0)]) - } - fn entries<'a>(&self, model: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - Box::new([entry::model(0, model)]) - } -} -impl BuildCombine for Skin { - type Resource<'a> = &'a Buffer; - - fn label(&self) -> String { - "skinned".to_owned() - } - fn layout_entries(&self, _: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - Box::new([layout_entry::skinning(1)]) - } - fn entries<'a>(&self, skin: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - Box::new([entry::skinning(1, skin)]) - } -} -impl BuildCombine for Morph { - type Resource<'a> = (AssetId, &'a Buffer, &'a TextureView); - fn label(&self) -> String { - "morphed".to_owned() - } - fn layout_entries(&self, _: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - Box::new([layout_entry::weights(2), layout_entry::targets(3)]) - } - fn entries<'a>(&self, (_, weights, targets): Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - Box::new([entry::weights(2, weights), entry::targets(3, targets)]) - } -} -impl BuildCombine for MvMorph { - type Resource<'a> = &'a Buffer; + fn layout_label(self) -> &'static str { + let labels = MeshBindings { + model_only: "mesh_layout", + morphed: "morphed_mesh_layout", + skinned: "skinned_mesh_layout", + morphed_skinned: "morphed_skinned_mesh_layout", + morphed_mvectors: "morphed_mvectors_mesh_layout", + skinned_mvectors: "skinned_mvectors_mesh_layout", + morphed_skinned_mvectors: "morphed_skinned_mvectors_mesh_layout", + }; + labels.get_binding(self) + } + fn bind_group_label(self) -> &'static str { + let labels = MeshBindings { + model_only: "mesh_bind_group", + morphed: "morphed_mesh_bind_group", + skinned: "skinned_mesh_bind_group", + morphed_skinned: "morphed_skinned_mesh_bind_group", + morphed_mvectors: "morphed_mvectors_mesh_bind_group", + skinned_mvectors: "skinned_mvectors_mesh_bind_group", + morphed_skinned_mvectors: "morphed_skinned_mvectors_mesh_bind_group", + }; + labels.get_binding(self) + } + fn layout(&self, device: &RenderDevice) -> BindGroupLayout { + let mut entries = SmallVec::<[_; 6]>::new(); + entries.extend(model_layout_entries(device)); - fn label(&self) -> String { - "mvectors_morphed".to_owned() - } - fn layout_entries(&self, _: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - Box::new([layout_entry::weights(2 + MV_OFFSET)]) - } - fn entries<'a>(&self, weights: Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - Box::new([entry::weights(2 + MV_OFFSET, weights)]) - } -} - -macro_rules! impl_tuple_build_combine { - ($(($T:ident, $s:ident, $r:ident)),*) => { - impl<$($T: BuildCombine),*> BuildCombine for ($($T),*) { - type Resource<'a> = ($($T::Resource<'a>),*); - - fn label(&self) -> String { - let ($($s),*) = self; - [$($s.label()),*].join("_") - } - fn layout_entries(&self, device: &RenderDevice) -> Box<[BindGroupLayoutEntry]> { - let ($($s),*) = self; - [$($s.layout_entries(device)),*] - .into_iter().flat_map(|t| t.to_vec()).collect::>().into() - } - fn entries<'a>(&self, ($($r),*): Self::Resource<'a>) -> Box<[BindGroupEntry<'a>]> { - let ($($s),*) = self; - [$($s.entries($r)),*] - .into_iter().flat_map(|t| t.to_vec()).collect::>().into() - } + if self.morph { + entries.extend(morph_layout_entries()); } - } -} -all_tuples!(impl_tuple_build_combine, 2, 5, T, s, r); - -type MorphBinding = HashMap, BindGroup>; -type MeshBindings = ( - Model, - Option, - Option, - Option, - Option>, -); -const fn combination(is_morph: bool, is_skin: bool, is_mvectors: bool) -> usize { - match (is_morph, is_skin, is_mvectors) { - (false, false, _) => 0, - (false, true, false) => 1, - (true, false, false) => 2, - (true, true, false) => 3, - (false, true, true) => 4, - (true, false, true) => 5, - (true, true, true) => 6, - } -} -const fn somv(t: T) -> Option> { - Some(Mv(t)) -} -const COMBINATIONS: [MeshBindings; 7] = [ - (Model, None, None, None, None), - (Model, None, Some(Skin), None, None), - (Model, Some(Morph), None, None, None), - (Model, Some(Morph), Some(Skin), None, None), - (Model, None, Some(Skin), None, somv(Skin)), - (Model, Some(Morph), None, Some(MvMorph), None), - (Model, Some(Morph), Some(Skin), Some(MvMorph), somv(Skin)), -]; - -#[derive(Default, Resource)] -pub struct MeshBindGroups( - Option, - Option, - MorphBinding, - MorphBinding, - Option, - MorphBinding, - MorphBinding, -); -impl MeshBindGroups { - fn clear(&mut self) { - self.0 = None; - self.1 = None; - self.2.clear(); - self.3.clear(); - self.4 = None; - self.5.clear(); - self.6.clear(); - } - pub fn get( - &self, - id: Option>, - is_skin: bool, - is_mvectors: bool, - ) -> Option<&BindGroup> { - match (&id, is_skin, is_mvectors) { - (None, false, _) => self.0.as_ref(), - (None, true, false) => self.1.as_ref(), - (Some(id), false, false) => self.2.get(id), - (Some(id), true, false) => self.3.get(id), - (None, true, true) => self.4.as_ref(), - (Some(id), false, true) => self.5.get(id), - (Some(id), true, true) => self.6.get(id), + if self.skin { + entries.extend(skin_layout_entries()); } + if self.morph && self.mvectors { + entries.extend(mvectors_morph_layout_entries()); + } + if self.skin && self.mvectors { + entries.extend(mvectors_skin_layout_entries()); + } + device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &entries, + label: Some(self.layout_label()), + }) } } -pub struct MeshBindingsUpdater<'a> { - bind_groups: &'a mut MeshBindGroups, +pub struct MeshBindGroupMaker<'a> { layouts: &'a MeshLayouts, device: &'a RenderDevice, model: BindingResource<'a>, + bind_groups: &'a mut MeshBindGroups, } -impl<'a> MeshBindingsUpdater<'a> { - pub fn insert( +impl<'a> MeshBindGroupMaker<'a> { + pub fn add( &mut self, + morph: Option<(AssetId, &Buffer, &TextureView)>, skin: Option<&Buffer>, - weights: Option<(AssetId, &Buffer, &TextureView)>, - previous_skin: Option<&Buffer>, previous_weights: Option<&Buffer>, + previous_skin: Option<&Buffer>, ) { - let is_skin = skin.is_some(); - let is_morph = weights.is_some(); - let is_mvectors = previous_skin.is_some() || previous_weights.is_some(); - let index = combination(is_morph, is_skin, is_mvectors); - let bindings = &COMBINATIONS[index]; + let mut entries = SmallVec::<[_; 6]>::new(); + entries.extend(model_entries(self.model.clone())); + + if let Some((_, weights, targets)) = morph { + entries.extend(morph_entries(weights, targets)); + } + if let Some(skin) = skin { + entries.extend(skin_entries(skin)); + } + if let Some(previous_weights) = previous_weights { + entries.extend(mvectors_morph_entries(previous_weights)); + } + if let Some(previous_skin) = previous_skin { + entries.extend(mvectors_skin_entries(previous_skin)); + } + let skin = skin.is_some(); + let mvectors = previous_weights.is_some() || previous_skin.is_some(); + + let binding = Binding::new(morph.is_some(), skin, mvectors); - let model = self.model.clone(); let bind_group = self.device.create_bind_group(&BindGroupDescriptor { - entries: &bindings.entries((model, weights, skin, previous_skin, previous_weights)), - layout: &self.layouts.layouts[index], - label: Some(&format!("{}_bind_group", bindings.label())), + entries: &entries, + label: Some(binding.bind_group_label()), + layout: self.layouts.get_binding(binding), }); - match (weights.map(|i| i.0), is_skin, is_mvectors) { - (None, false, _) => { - self.bind_groups.0 = Some(bind_group); - } - (None, true, false) => { - self.bind_groups.1 = Some(bind_group); - } - (Some(id), false, false) => { - self.bind_groups.2.insert(id, bind_group); - } - (Some(id), true, false) => { - self.bind_groups.3.insert(id, bind_group); - } - (None, true, true) => { - self.bind_groups.4 = Some(bind_group); - } - (Some(id), false, true) => { - self.bind_groups.5.insert(id, bind_group); - } - (Some(id), true, true) => { - self.bind_groups.6.insert(id, bind_group); - } - } + self.bind_groups + .insert(morph.map(|m| m.0), skin, mvectors, bind_group); } } diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index fc18bb63c73b7f..64010634bb850b 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -237,7 +237,7 @@ impl SpecializedMeshPipeline for CustomPipeline { type DrawCustom = ( SetItemPipeline, SetMeshViewBindGroup<0>, - SetMeshBindGroup<1>, + SetMeshBindGroup<1, false>, DrawMeshInstanced, );