Skip to content

Commit

Permalink
Build batches across phases in parallel. (#17764)
Browse files Browse the repository at this point in the history
Currently, invocations of `batch_and_prepare_binned_render_phase` and
`batch_and_prepare_sorted_render_phase` can't run in parallel because
they write to scene-global GPU buffers. After PR #17698,
`batch_and_prepare_binned_render_phase` started accounting for the
lion's share of the CPU time, causing us to be strongly CPU bound on
scenes like Caldera when occlusion culling was on (because of the
overhead of batching for the Z-prepass). Although I eventually plan to
optimize `batch_and_prepare_binned_render_phase`, we can obtain
significant wins now by parallelizing that system across phases.

This commit splits all GPU buffers that
`batch_and_prepare_binned_render_phase` and
`batch_and_prepare_sorted_render_phase` touches into separate buffers
for each phase so that the scheduler will run those phases in parallel.
At the end of batch preparation, we gather the render phases up into a
single resource with a new *collection* phase. Because we already run
mesh preprocessing separately for each phase in order to make occlusion
culling work, this is actually a cleaner separation. For example, mesh
output indices (the unique ID that identifies each mesh instance on GPU)
are now guaranteed to be sequential starting from 0, which will simplify
the forthcoming work to remove them in favor of the compute dispatch ID.

On Caldera, this brings the frame time down to approximately 9.1 ms with
occlusion culling on.

![Screenshot 2025-02-08
210720](https://github.com/user-attachments/assets/44bed500-e323-4786-b40c-828b75bc7d3f)
  • Loading branch information
pcwalton authored Feb 13, 2025
1 parent 62c1812 commit 0ede857
Show file tree
Hide file tree
Showing 15 changed files with 962 additions and 449 deletions.
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/decal/forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use bevy_render::{
AsBindGroup, CompareFunction, RenderPipelineDescriptor, Shader,
SpecializedMeshPipelineError,
},
RenderDebugFlags,
};

const FORWARD_DECAL_MESH_HANDLE: Handle<Mesh> =
Expand Down Expand Up @@ -48,6 +49,7 @@ impl Plugin for ForwardDecalPlugin {
app.add_plugins(MaterialPlugin::<ForwardDecalMaterial<StandardMaterial>> {
prepass_enabled: false,
shadows_enabled: false,
debug_flags: RenderDebugFlags::default(),
..Default::default()
});
}
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ use bevy_render::{
sync_component::SyncComponentPlugin,
texture::GpuImage,
view::VisibilitySystems,
ExtractSchedule, Render, RenderApp, RenderSet,
ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderSet,
};

use bevy_transform::TransformSystem;
Expand Down Expand Up @@ -182,6 +182,8 @@ pub struct PbrPlugin {
/// This requires compute shader support and so will be forcibly disabled if
/// the platform doesn't support those.
pub use_gpu_instance_buffer_builder: bool,
/// Debugging flags that can optionally be set when constructing the renderer.
pub debug_flags: RenderDebugFlags,
}

impl Default for PbrPlugin {
Expand All @@ -190,6 +192,7 @@ impl Default for PbrPlugin {
prepass_enabled: true,
add_default_deferred_lighting_plugin: true,
use_gpu_instance_buffer_builder: true,
debug_flags: RenderDebugFlags::default(),
}
}
}
Expand Down Expand Up @@ -333,9 +336,11 @@ impl Plugin for PbrPlugin {
.add_plugins((
MeshRenderPlugin {
use_gpu_instance_buffer_builder: self.use_gpu_instance_buffer_builder,
debug_flags: self.debug_flags,
},
MaterialPlugin::<StandardMaterial> {
prepass_enabled: self.prepass_enabled,
debug_flags: self.debug_flags,
..Default::default()
},
ScreenSpaceAmbientOcclusionPlugin,
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ pub struct MaterialPlugin<M: Material> {
pub prepass_enabled: bool,
/// Controls if shadows are enabled for the Material.
pub shadows_enabled: bool,
/// Debugging flags that can optionally be set when constructing the renderer.
pub debug_flags: RenderDebugFlags,
pub _marker: PhantomData<M>,
}

Expand All @@ -260,6 +262,7 @@ impl<M: Material> Default for MaterialPlugin<M> {
Self {
prepass_enabled: true,
shadows_enabled: true,
debug_flags: RenderDebugFlags::default(),
_marker: Default::default(),
}
}
Expand Down Expand Up @@ -374,7 +377,7 @@ where
}

if self.prepass_enabled {
app.add_plugins(PrepassPlugin::<M>::default());
app.add_plugins(PrepassPlugin::<M>::new(self.debug_flags));
}
}

Expand Down
24 changes: 17 additions & 7 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use bevy_render::{
renderer::RenderAdapter,
sync_world::RenderEntity,
view::{RenderVisibilityRanges, VISIBILITY_RANGES_STORAGE_BUFFER_COUNT},
ExtractSchedule, Render, RenderApp, RenderSet,
ExtractSchedule, Render, RenderApp, RenderDebugFlags, RenderSet,
};
pub use prepass_bindings::*;

Expand Down Expand Up @@ -146,11 +146,19 @@ where
/// Sets up the prepasses for a [`Material`].
///
/// This depends on the [`PrepassPipelinePlugin`].
pub struct PrepassPlugin<M: Material>(PhantomData<M>);
pub struct PrepassPlugin<M: Material> {
/// Debugging flags that can optionally be set when constructing the renderer.
pub debug_flags: RenderDebugFlags,
pub phantom: PhantomData<M>,
}

impl<M: Material> Default for PrepassPlugin<M> {
fn default() -> Self {
Self(Default::default())
impl<M: Material> PrepassPlugin<M> {
/// Creates a new [`PrepassPlugin`] with the given debug flags.
pub fn new(debug_flags: RenderDebugFlags) -> Self {
PrepassPlugin {
debug_flags,
phantom: PhantomData,
}
}
}

Expand All @@ -176,8 +184,10 @@ where
),
)
.add_plugins((
BinnedRenderPhasePlugin::<Opaque3dPrepass, MeshPipeline>::default(),
BinnedRenderPhasePlugin::<AlphaMask3dPrepass, MeshPipeline>::default(),
BinnedRenderPhasePlugin::<Opaque3dPrepass, MeshPipeline>::new(self.debug_flags),
BinnedRenderPhasePlugin::<AlphaMask3dPrepass, MeshPipeline>::new(
self.debug_flags,
),
));
}

Expand Down
Loading

0 comments on commit 0ede857

Please sign in to comment.