From 5f05e75a70e4e3c4dff64d3efcc2b8900e2bf1c4 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Mon, 15 Apr 2024 07:00:43 +0200 Subject: [PATCH] Fix 2D BatchedInstanceBuffer clear (#12922) # Objective - `cargo run --release --example bevymark -- --benchmark --waves 160 --per-wave 1000 --mode mesh2d` runs slower and slower over time due to `no_gpu_preprocessing::write_batched_instance_buffer` taking longer and longer because the `BatchedInstanceBuffer` is not cleared ## Solution - Split the `clear_batched_instance_buffers` system into CPU and GPU versions - Use the CPU version for 2D meshes --- crates/bevy_pbr/src/render/mesh.rs | 15 ++++++++--- .../src/batching/gpu_preprocessing.rs | 20 +++++++++++++- crates/bevy_render/src/batching/mod.rs | 26 +------------------ .../src/batching/no_gpu_preprocessing.rs | 13 ++++++++++ crates/bevy_sprite/src/mesh2d/mesh.rs | 6 ++++- 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index ebe7274f7b..35e75b9341 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -15,8 +15,8 @@ use bevy_ecs::{ use bevy_math::{Affine3, Rect, UVec2, Vec3, Vec4}; use bevy_render::{ batching::{ - clear_batched_instance_buffers, gpu_preprocessing, no_gpu_preprocessing, GetBatchData, - GetFullBatchData, NoAutomaticBatching, + gpu_preprocessing, no_gpu_preprocessing, GetBatchData, GetFullBatchData, + NoAutomaticBatching, }, mesh::*, render_asset::RenderAssets, @@ -139,10 +139,14 @@ impl Plugin for MeshRenderPlugin { .init_resource::() .init_resource::() .init_resource::() - .add_systems(ExtractSchedule, (extract_skins, extract_morphs)) .add_systems( ExtractSchedule, - clear_batched_instance_buffers::.before(ExtractMeshesSet), + ( + extract_skins, + extract_morphs, + gpu_preprocessing::clear_batched_gpu_instance_buffers:: + .before(ExtractMeshesSet), + ), ) .add_systems( Render, @@ -151,6 +155,9 @@ impl Plugin for MeshRenderPlugin { prepare_morphs.in_set(RenderSet::PrepareResources), prepare_mesh_bind_group.in_set(RenderSet::PrepareBindGroups), prepare_mesh_view_bind_groups.in_set(RenderSet::PrepareBindGroups), + no_gpu_preprocessing::clear_batched_cpu_instance_buffers:: + .in_set(RenderSet::Cleanup) + .after(RenderSet::Render), ), ); } diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index 8757943419..22bebd8bf9 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -125,10 +125,28 @@ where } } +/// A system that runs early in extraction and clears out all the +/// [`BatchedInstanceBuffers`] for the frame. +/// +/// We have to run this during extraction because, if GPU preprocessing is in +/// use, the extraction phase will write to the mesh input uniform buffers +/// directly, so the buffers need to be cleared before then. +pub fn clear_batched_gpu_instance_buffers( + gpu_batched_instance_buffers: Option< + ResMut>, + >, +) where + GFBD: GetFullBatchData, +{ + if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers { + gpu_batched_instance_buffers.clear(); + } +} + /// A system that removes GPU preprocessing work item buffers that correspond to /// deleted [`ViewTarget`]s. /// -/// This is a separate system from [`super::clear_batched_instance_buffers`] +/// This is a separate system from [`clear_batched_gpu_instance_buffers`] /// because [`ViewTarget`]s aren't created until after the extraction phase is /// completed. pub fn delete_old_work_item_buffers( diff --git a/crates/bevy_render/src/batching/mod.rs b/crates/bevy_render/src/batching/mod.rs index 6811451f37..87bfc4a24e 100644 --- a/crates/bevy_render/src/batching/mod.rs +++ b/crates/bevy_render/src/batching/mod.rs @@ -1,7 +1,7 @@ use bevy_ecs::{ component::Component, entity::Entity, - system::{Query, ResMut, SystemParam, SystemParamItem}, + system::{Query, SystemParam, SystemParamItem}, }; use bytemuck::Pod; use nonmax::NonMaxU32; @@ -135,30 +135,6 @@ pub trait GetFullBatchData: GetBatchData { ) -> Option; } -/// A system that runs early in extraction and clears out all the -/// [`gpu_preprocessing::BatchedInstanceBuffers`] for the frame. -/// -/// We have to run this during extraction because, if GPU preprocessing is in -/// use, the extraction phase will write to the mesh input uniform buffers -/// directly, so the buffers need to be cleared before then. -pub fn clear_batched_instance_buffers( - cpu_batched_instance_buffer: Option< - ResMut>, - >, - gpu_batched_instance_buffers: Option< - ResMut>, - >, -) where - GFBD: GetFullBatchData, -{ - if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer { - cpu_batched_instance_buffer.clear(); - } - if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers { - gpu_batched_instance_buffers.clear(); - } -} - /// Sorts a render phase that uses bins. pub fn sort_binned_render_phase(mut views: Query<&mut BinnedRenderPhase>) where diff --git a/crates/bevy_render/src/batching/no_gpu_preprocessing.rs b/crates/bevy_render/src/batching/no_gpu_preprocessing.rs index 429fe5bb45..3387243d13 100644 --- a/crates/bevy_render/src/batching/no_gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/no_gpu_preprocessing.rs @@ -43,6 +43,19 @@ where } } +/// A system that clears out the [`BatchedInstanceBuffer`] for the frame. +/// +/// This needs to run before the CPU batched instance buffers are used. +pub fn clear_batched_cpu_instance_buffers( + cpu_batched_instance_buffer: Option>>, +) where + GBD: GetBatchData, +{ + if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer { + cpu_batched_instance_buffer.clear(); + } +} + /// Batch the items in a sorted render phase, when GPU instance buffer building /// isn't in use. This means comparing metadata needed to draw each phase item /// and trying to combine the draws into a batch. diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 257ad61dfb..2e61072fa8 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -12,7 +12,8 @@ use bevy_ecs::{ use bevy_math::{Affine3, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::batching::no_gpu_preprocessing::{ - batch_and_prepare_sorted_render_phase, write_batched_instance_buffer, BatchedInstanceBuffer, + self, batch_and_prepare_sorted_render_phase, write_batched_instance_buffer, + BatchedInstanceBuffer, }; use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef}; use bevy_render::{ @@ -107,6 +108,9 @@ impl Plugin for Mesh2dRenderPlugin { .in_set(RenderSet::PrepareResourcesFlush), prepare_mesh2d_bind_group.in_set(RenderSet::PrepareBindGroups), prepare_mesh2d_view_bind_groups.in_set(RenderSet::PrepareBindGroups), + no_gpu_preprocessing::clear_batched_cpu_instance_buffers:: + .in_set(RenderSet::Cleanup) + .after(RenderSet::Render), ), ); }