Skip to content

Commit

Permalink
Try to fix motion blur properly.
Browse files Browse the repository at this point in the history
  • Loading branch information
pcwalton committed Feb 11, 2025
1 parent f202a12 commit ea2029d
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 118 deletions.
1 change: 1 addition & 0 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
66 changes: 45 additions & 21 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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,
Expand Down Expand Up @@ -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::<Opaque3d, MeshPipeline>::default(),
Expand Down Expand Up @@ -478,6 +488,8 @@ pub struct MeshUniform {
pub first_vertex_index: 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.
Expand Down Expand Up @@ -515,11 +527,6 @@ pub struct MeshInputUniform {
///
/// This is used for TAA. If not present, this will be `u32::MAX`.
pub previous_input_index: u32,
/// The frame number of the previous frame.
///
/// The shader compared this field to the current frame number in order to
/// determine whether [`Self::previous_input_index`] is valid.
pub previous_input_frame_count: u32,
/// The index of this mesh's first vertex in the vertex buffer.
///
/// Multiple meshes can be packed into a single vertex buffer (see
Expand All @@ -539,15 +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,
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.
Expand Down Expand Up @@ -580,6 +587,7 @@ impl MeshUniform {
material_bind_group_slot: MaterialBindGroupSlot,
maybe_lightmap: Option<(LightmapSlotIndex, Rect)>,
current_skin_index: Option<u32>,
previous_skin_index: Option<u32>,
tag: Option<u32>,
) -> Self {
let (local_from_world_transpose_a, local_from_world_transpose_b) =
Expand All @@ -598,6 +606,7 @@ impl MeshUniform {
flags: mesh_transforms.flags,
first_vertex_index,
current_skin_index: current_skin_index.unwrap_or(u32::MAX),
previous_skin_index: previous_skin_index.unwrap_or(u32::MAX),
material_and_lightmap_bind_group_slot: u32::from(material_bind_group_slot)
| ((lightmap_bind_group_slot as u32) << 16),
tag: tag.unwrap_or(0),
Expand Down Expand Up @@ -1103,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) {
Expand All @@ -1121,8 +1131,11 @@ impl RenderMeshInstanceGpuBuilder {
),
None => (false, 0, 0),
};

let current_skin_index = match skin_indices.get(&entity) {
let current_skin_index = match skin_indices.current.get(&entity) {
Some(skin_indices) => skin_indices.index(),
None => u32::MAX,
};
let previous_skin_index = match skin_indices.prev.get(&entity) {
Some(skin_indices) => skin_indices.index(),
None => u32::MAX,
};
Expand Down Expand Up @@ -1151,7 +1164,7 @@ impl RenderMeshInstanceGpuBuilder {
lightmap_uv_rect: self.lightmap_uv_rect,
flags: self.mesh_flags.bits(),
previous_input_index: u32::MAX,
previous_input_frame_count: u32::MAX,
timestamp: timestamp.0,
first_vertex_index,
first_index_index,
index_count: if mesh_is_indexed {
Expand All @@ -1160,11 +1173,11 @@ impl RenderMeshInstanceGpuBuilder {
vertex_count
},
current_skin_index,
previous_skin_index,
material_and_lightmap_bind_group_slot: u32::from(
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?
Expand Down Expand Up @@ -1566,7 +1579,7 @@ fn set_mesh_motion_vector_flags(
skin_indices: Res<SkinIndices>,
morph_indices: Res<MorphIndices>,
) {
for &entity in skin_indices.keys() {
for &entity in skin_indices.prev.keys() {
render_mesh_instances
.insert_mesh_instance_flags(entity, RenderMeshInstanceFlags::HAS_PREVIOUS_SKIN);
}
Expand All @@ -1590,6 +1603,7 @@ pub fn collect_meshes_for_gpu_building(
render_material_bindings: Res<RenderMaterialBindings>,
render_lightmaps: Res<RenderLightmaps>,
skin_indices: Res<SkinIndices>,
frame_count: Res<FrameCount>,
) {
let RenderMeshInstances::GpuBuilding(ref mut render_mesh_instances) =
render_mesh_instances.into_inner()
Expand Down Expand Up @@ -1629,6 +1643,7 @@ pub fn collect_meshes_for_gpu_building(
&render_material_bindings,
&render_lightmaps,
&skin_indices,
*frame_count,
);
}

Expand Down Expand Up @@ -1656,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);
Expand Down Expand Up @@ -1836,7 +1852,8 @@ impl GetBatchData for MeshPipeline {
};
let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);

let current_skin_index = skin_indices.get(&main_entity).map(SkinIndex::index);
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((
Expand All @@ -1846,6 +1863,7 @@ impl GetBatchData for MeshPipeline {
material_bind_group_index.slot,
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
current_skin_index,
previous_skin_index,
Some(mesh_instance.tag),
),
mesh_instance.should_batch().then_some((
Expand Down Expand Up @@ -1904,14 +1922,16 @@ impl GetFullBatchData for MeshPipeline {
};
let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity);

let current_skin_index = skin_indices.get(&main_entity).map(SkinIndex::index);
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);

Some(MeshUniform::new(
&mesh_instance.transforms,
first_vertex_index,
mesh_instance.material_bindings_index.slot,
maybe_lightmap.map(|lightmap| (lightmap.slot_index, lightmap.uv_rect)),
current_skin_index,
previous_skin_index,
Some(mesh_instance.tag),
))
}
Expand Down Expand Up @@ -2799,7 +2819,8 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
return RenderCommandResult::Success;
};

let current_skin_index = skin_indices.get(entity);
let current_skin_index = skin_indices.current.get(entity);
let prev_skin_index = skin_indices.prev.get(entity);
let current_morph_index = morph_indices.current.get(entity);
let prev_morph_index = morph_indices.prev.get(entity);

Expand Down Expand Up @@ -2846,11 +2867,14 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
if has_motion_vector_prepass {
// Attach the previous skin index for motion vector computation. If
// there isn't one, just use zero as the shader will ignore it.
if skin::skins_use_uniform_buffers(&render_device) {
if let Some(current_skin_index) = current_skin_index {
dynamic_offsets[offset_count] = current_skin_index.byte_offset;
offset_count += 1;
if current_skin_index.is_some() && skin::skins_use_uniform_buffers(&render_device) {
match prev_skin_index {
Some(prev_skin_index) => {
dynamic_offsets[offset_count] = prev_skin_index.byte_offset;
}
None => dynamic_offsets[offset_count] = 0,
}
offset_count += 1;
}

// Attach the previous morph index for motion vector computation. If
Expand Down
15 changes: 12 additions & 3 deletions crates/bevy_pbr/src/render/mesh_preprocess.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,14 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
}
#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_frame_count = current_input[input_index].previous_input_frame_count;
let previous_input_index = current_input[input_index].previous_input_index;
let previous_input_is_valid = previous_input_frame_count + 1 == view.frame_count;
var previous_world_from_local_affine_transpose: mat3x4<f32>;
if (previous_input_is_valid && previous_input_index != 0xffffffffu) {
if (mesh_changed_this_frame && previous_input_index != 0xffffffffu) {
previous_world_from_local_affine_transpose =
previous_input[previous_input_index].world_from_local;
} else {
Expand All @@ -206,6 +208,12 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
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
Expand Down Expand Up @@ -344,6 +352,7 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
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 = 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;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/render/mesh_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct Mesh {
// The index of the mesh's first vertex in the vertex buffer.
first_vertex_index: u32,
current_skin_index: u32,
previous_skin_index: u32,
// 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,
Expand Down
Loading

0 comments on commit ea2029d

Please sign in to comment.