diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index bf568885e7fef..5031d7c91444f 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -37,6 +37,7 @@ bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev" } bevy_color = { path = "../bevy_color", version = "0.16.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.16.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" } +bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.16.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" } bevy_image = { path = "../bevy_image", version = "0.16.0-dev" } bevy_math = { path = "../bevy_math", version = "0.16.0-dev" } diff --git a/crates/bevy_pbr/src/meshlet/instance_manager.rs b/crates/bevy_pbr/src/meshlet/instance_manager.rs index f615114d417a1..26f6432a1fdf7 100644 --- a/crates/bevy_pbr/src/meshlet/instance_manager.rs +++ b/crates/bevy_pbr/src/meshlet/instance_manager.rs @@ -125,7 +125,6 @@ impl InstanceManager { None, None, None, - None, ); // Append instance data diff --git a/crates/bevy_pbr/src/render/gpu_preprocess.rs b/crates/bevy_pbr/src/render/gpu_preprocess.rs index 26559f9223dd5..7cd757a2e5c25 100644 --- a/crates/bevy_pbr/src/render/gpu_preprocess.rs +++ b/crates/bevy_pbr/src/render/gpu_preprocess.rs @@ -626,12 +626,10 @@ impl Node for EarlyGpuPreprocessNode { continue; }; - // If we're drawing indirectly, make sure the mesh preprocessing - // shader has access to the view info it needs to do culling. - let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![]; - if !no_indirect_drawing { - dynamic_offsets.push(view_uniform_offset.offset); - } + // Make sure the mesh preprocessing shader has access to the + // view info it needs to do culling and motion vector + // computation. + let dynamic_offsets = [view_uniform_offset.offset]; // Are we drawing directly or indirectly? match *phase_bind_groups { @@ -1317,6 +1315,11 @@ fn preprocess_direct_bind_group_layout_entries() -> DynamicBindGroupLayoutEntrie DynamicBindGroupLayoutEntries::new_with_indices( ShaderStages::COMPUTE, ( + // `view` + ( + 0, + uniform_buffer::(/* has_dynamic_offset= */ true), + ), // `current_input` (3, storage_buffer_read_only::(false)), // `previous_input` @@ -1361,11 +1364,6 @@ fn gpu_culling_bind_group_layout_entries() -> DynamicBindGroupLayoutEntries { 8, storage_buffer_read_only::(/* has_dynamic_offset= */ false), ), - // `view` - ( - 0, - uniform_buffer::(/* has_dynamic_offset= */ true), - ), )) } @@ -1802,11 +1800,14 @@ impl<'a> PreprocessBindGroupBuilder<'a> { ) .ok(); + let view_uniforms_binding = self.view_uniforms.uniforms.binding()?; + Some(PhasePreprocessBindGroups::Direct( self.render_device.create_bind_group( "preprocess_direct_bind_group", &self.pipelines.direct_preprocess.bind_group_layout, &BindGroupEntries::with_indices(( + (0, view_uniforms_binding), (3, self.current_input_buffer.as_entire_binding()), (4, self.previous_input_buffer.as_entire_binding()), ( diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index a02b56e441a37..34d0debbb7c6b 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,4 +1,7 @@ -use crate::material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot}; +use crate::{ + material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot}, + skin::mark_meshes_as_changed_if_their_skins_changed, +}; use allocator::MeshAllocator; use bevy_asset::{load_internal_asset, AssetId, UntypedAssetId}; use bevy_core_pipeline::{ @@ -8,6 +11,7 @@ use bevy_core_pipeline::{ prepass::MotionVectorPrepass, }; use bevy_derive::{Deref, DerefMut}; +use bevy_diagnostic::FrameCount; use bevy_ecs::{ prelude::*, query::ROQueryItem, @@ -163,7 +167,13 @@ impl Plugin for MeshRenderPlugin { app.add_systems( PostUpdate, - (no_automatic_skin_batching, no_automatic_morph_batching), + ( + no_automatic_skin_batching, + no_automatic_morph_batching, + mark_meshes_as_changed_if_their_skins_changed + .ambiguous_with_all() + .after(mark_3d_meshes_as_changed_if_their_assets_changed), + ), ) .add_plugins(( BinnedRenderPhasePlugin::::default(), @@ -536,17 +546,15 @@ pub struct MeshInputUniform { pub index_count: u32, /// The current skin index, or `u32::MAX` if there's no skin. pub current_skin_index: u32, - /// The previous skin index, or `u32::MAX` if there's no previous skin. pub previous_skin_index: u32, /// The material and lightmap indices, packed into 32 bits. /// /// Low 16 bits: index of the material inside the bind group data. /// High 16 bits: index of the lightmap in the binding array. pub material_and_lightmap_bind_group_slot: u32, + pub timestamp: u32, /// User supplied tag to identify this mesh instance. pub tag: u32, - /// Padding. - pub pad: u32, } /// Information about each mesh instance needed to cull it on GPU. @@ -1104,6 +1112,7 @@ impl RenderMeshInstanceGpuBuilder { render_material_bindings: &RenderMaterialBindings, render_lightmaps: &RenderLightmaps, skin_indices: &SkinIndices, + timestamp: FrameCount, ) -> u32 { let (first_vertex_index, vertex_count) = match mesh_allocator.mesh_vertex_slice(&self.shared.mesh_asset_id) { @@ -1122,7 +1131,6 @@ impl RenderMeshInstanceGpuBuilder { ), None => (false, 0, 0), }; - let current_skin_index = match skin_indices.current.get(&entity) { Some(skin_indices) => skin_indices.index(), None => u32::MAX, @@ -1156,6 +1164,7 @@ impl RenderMeshInstanceGpuBuilder { lightmap_uv_rect: self.lightmap_uv_rect, flags: self.mesh_flags.bits(), previous_input_index: u32::MAX, + timestamp: timestamp.0, first_vertex_index, first_index_index, index_count: if mesh_is_indexed { @@ -1169,7 +1178,6 @@ impl RenderMeshInstanceGpuBuilder { self.shared.material_bindings_index.slot, ) | ((lightmap_slot as u32) << 16), tag: self.shared.tag, - pad: 0, }; // Did the last frame contain this entity as well? @@ -1595,6 +1603,7 @@ pub fn collect_meshes_for_gpu_building( render_material_bindings: Res, render_lightmaps: Res, skin_indices: Res, + frame_count: Res, ) { let RenderMeshInstances::GpuBuilding(ref mut render_mesh_instances) = render_mesh_instances.into_inner() @@ -1634,6 +1643,7 @@ pub fn collect_meshes_for_gpu_building( &render_material_bindings, &render_lightmaps, &skin_indices, + *frame_count, ); } @@ -1661,6 +1671,7 @@ pub fn collect_meshes_for_gpu_building( &render_material_bindings, &render_lightmaps, &skin_indices, + *frame_count, ); mesh_culling_builder .update(&mut mesh_culling_data_buffer, instance_data_index as usize); @@ -1843,7 +1854,6 @@ impl GetBatchData for MeshPipeline { let current_skin_index = skin_indices.current.get(&main_entity).map(SkinIndex::index); let previous_skin_index = skin_indices.prev.get(&main_entity).map(SkinIndex::index); - let material_bind_group_index = mesh_instance.material_bindings_index; Some(( diff --git a/crates/bevy_pbr/src/render/mesh_preprocess.wgsl b/crates/bevy_pbr/src/render/mesh_preprocess.wgsl index 9bbe96e80d633..932bd58a1aaab 100644 --- a/crates/bevy_pbr/src/render/mesh_preprocess.wgsl +++ b/crates/bevy_pbr/src/render/mesh_preprocess.wgsl @@ -192,18 +192,28 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3) { } #endif + // Was the mesh transform updated this frame? + let timestamp = current_input[input_index].timestamp; + let mesh_changed_this_frame = timestamp == view.frame_count; + // Look up the previous model matrix. let previous_input_index = current_input[input_index].previous_input_index; var previous_world_from_local_affine_transpose: mat3x4; - if (previous_input_index == 0xffffffff) { - previous_world_from_local_affine_transpose = world_from_local_affine_transpose; - } else { + if (mesh_changed_this_frame && previous_input_index != 0xffffffffu) { previous_world_from_local_affine_transpose = previous_input[previous_input_index].world_from_local; + } else { + previous_world_from_local_affine_transpose = world_from_local_affine_transpose; } let previous_world_from_local = maths::affine3_to_square(previous_world_from_local_affine_transpose); + let previous_skin_index = select( + 0xffffffffu, + current_input[input_index].previous_skin_index, + mesh_changed_this_frame + ); + // Occlusion cull if necessary. This is done by calculating the screen-space // axis-aligned bounding box (AABB) of the mesh and testing it against the // appropriate level of the depth pyramid (a.k.a. hierarchical Z-buffer). If @@ -342,7 +352,7 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3) { output[mesh_output_index].lightmap_uv_rect = current_input[input_index].lightmap_uv_rect; output[mesh_output_index].first_vertex_index = current_input[input_index].first_vertex_index; output[mesh_output_index].current_skin_index = current_input[input_index].current_skin_index; - output[mesh_output_index].previous_skin_index = current_input[input_index].previous_skin_index; + output[mesh_output_index].previous_skin_index = previous_skin_index; output[mesh_output_index].material_and_lightmap_bind_group_slot = current_input[input_index].material_and_lightmap_bind_group_slot; output[mesh_output_index].tag = current_input[input_index].tag; diff --git a/crates/bevy_pbr/src/render/skin.rs b/crates/bevy_pbr/src/render/skin.rs index c248821ccafd3..c3082caf1e490 100644 --- a/crates/bevy_pbr/src/render/skin.rs +++ b/crates/bevy_pbr/src/render/skin.rs @@ -4,6 +4,7 @@ use std::sync::OnceLock; use bevy_asset::Assets; use bevy_ecs::prelude::*; use bevy_math::Mat4; +use bevy_render::mesh::Mesh3d; use bevy_render::sync_world::MainEntityHashMap; use bevy_render::{ batching::NoAutomaticBatching, @@ -25,7 +26,7 @@ use bevy_transform::prelude::GlobalTransform; pub const MAX_JOINTS: usize = 256; /// The location of the first joint matrix in the skin uniform buffer. -#[derive(Component)] +#[derive(Component, Clone, Copy)] pub struct SkinIndex { /// The byte offset of the first joint matrix. pub byte_offset: u32, @@ -232,3 +233,18 @@ pub fn no_automatic_skin_batching( commands.entity(entity).try_insert(NoAutomaticBatching); } } + +pub fn mark_meshes_as_changed_if_their_skins_changed( + mut skinned_meshes_query: Query<(&mut Mesh3d, &SkinnedMesh)>, + changed_joints_query: Query>, +) { + for (mut mesh, skinned_mesh) in &mut skinned_meshes_query { + if skinned_mesh + .joints + .iter() + .any(|&joint| changed_joints_query.contains(joint)) + { + mesh.set_changed(); + } + } +} diff --git a/crates/bevy_pbr/src/render/skinning.wgsl b/crates/bevy_pbr/src/render/skinning.wgsl index 92e977aeb1b92..038b293eed238 100644 --- a/crates/bevy_pbr/src/render/skinning.wgsl +++ b/crates/bevy_pbr/src/render/skinning.wgsl @@ -34,7 +34,7 @@ fn skin_model( + weights.z * joint_matrices.data[indexes.z] + weights.w * joint_matrices.data[indexes.w]; #else // SKINS_USE_UNIFORM_BUFFERS - let skin_index = mesh[instance_index].current_skin_index; + var skin_index = mesh[instance_index].current_skin_index; return weights.x * joint_matrices[skin_index + indexes.x] + weights.y * joint_matrices[skin_index + indexes.y] + weights.z * joint_matrices[skin_index + indexes.z] @@ -57,11 +57,15 @@ fn skin_prev_model( + weights.z * prev_joint_matrices.data[indexes.z] + weights.w * prev_joint_matrices.data[indexes.w]; #else // SKINS_USE_UNIFORM_BUFFERS - let skin_index = mesh[instance_index].previous_skin_index; - return weights.x * prev_joint_matrices[skin_index + indexes.x] - + weights.y * prev_joint_matrices[skin_index + indexes.y] - + weights.z * prev_joint_matrices[skin_index + indexes.z] - + weights.w * prev_joint_matrices[skin_index + indexes.w]; + let prev_skin_index = mesh[instance_index].previous_skin_index; + if (prev_skin_index == 0xffffffffu) { + return skin_model(indexed, weights, instance_index); + } + + return weights.x * prev_joint_matrices[prev_skin_index + indexes.x] + + weights.y * prev_joint_matrices[prev_skin_index + indexes.y] + + weights.z * prev_joint_matrices[prev_skin_index + indexes.z] + + weights.w * prev_joint_matrices[prev_skin_index + indexes.w]; #endif // SKINS_USE_UNIFORM_BUFFERS } diff --git a/crates/bevy_render/src/experimental/occlusion_culling/mesh_preprocess_types.wgsl b/crates/bevy_render/src/experimental/occlusion_culling/mesh_preprocess_types.wgsl index 6d1199adb4282..cbf9b2dc46338 100644 --- a/crates/bevy_render/src/experimental/occlusion_culling/mesh_preprocess_types.wgsl +++ b/crates/bevy_render/src/experimental/occlusion_culling/mesh_preprocess_types.wgsl @@ -19,9 +19,9 @@ struct MeshInput { // Low 16 bits: index of the material inside the bind group data. // High 16 bits: index of the lightmap in the binding array. material_and_lightmap_bind_group_slot: u32, + timestamp: u32, // User supplied index to identify the mesh instance tag: u32, - pad: u32, } // The `wgpu` indirect parameters structure. This is a union of two structures. diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 0feaa8487885b..32d06ab397578 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -103,6 +103,11 @@ impl RawBufferVec { self.values.append(&mut other.values); } + /// Returns the value at the given index. + pub fn get(&self, index: u32) -> Option<&T> { + self.values.get(index as usize) + } + /// Sets the value at the given index. /// /// The index must be less than [`RawBufferVec::len`]. diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index b0fab01d01b4e..ba0f8cca62ced 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -2,6 +2,7 @@ pub mod visibility; pub mod window; use bevy_asset::{load_internal_asset, weak_handle, Handle}; +use bevy_diagnostic::FrameCount; pub use visibility::*; pub use window::*; @@ -568,6 +569,7 @@ pub struct ViewUniform { pub frustum: [Vec4; 6], pub color_grading: ColorGradingUniform, pub mip_bias: f32, + pub frame_count: u32, } #[derive(Resource)] @@ -889,6 +891,7 @@ pub fn prepare_view_uniforms( Option<&TemporalJitter>, Option<&MipBias>, )>, + frame_count: Res, ) { let view_iter = views.iter(); let view_count = view_iter.len(); @@ -942,6 +945,7 @@ pub fn prepare_view_uniforms( frustum, color_grading: extracted_view.color_grading.clone().into(), mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, + frame_count: frame_count.0, }), }; diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index ed08599758a07..317de2eb88073 100644 --- a/crates/bevy_render/src/view/view.wgsl +++ b/crates/bevy_render/src/view/view.wgsl @@ -60,4 +60,5 @@ struct View { frustum: array, 6>, color_grading: ColorGrading, mip_bias: f32, + frame_count: u32, };