Skip to content

Commit

Permalink
Fix performance regression with shadow mapping (bevyengine#7914)
Browse files Browse the repository at this point in the history
# Objective

- @mockersf identified a performance regression of about 25% longer frame times introduced by bevyengine#7784 in a complex scene with the Amazon Lumberyard bistro scene with both exterior and interior variants and a number of point lights with shadow mapping enabled
  - The additional time seemed to be spent in the `ShadowPassNode`
  - `ShadowPassNode` encodes the draw commands for the shadow phase. Roughly the same numbers of entities were having draw commands encoded, so something about the way they were being encoded had changed.
  - One thing that definitely changed was that the pipeline used will be different depending on the alpha mode, and the scene has lots entities with opaque and blend materials. This suggested that maybe the pipeline was changing a lot so I tried a quick hack to see if it was the problem.

## Solution

- Sort the shadow phase items by their pipeline id
  - This groups phase items by their pipeline id, which significantly reduces pipeline rebinding required to the point that the performance regression was gone.
  • Loading branch information
superdump authored and Shfty committed Mar 19, 2023
1 parent 3e251e0 commit 13e47fe
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 4 deletions.
10 changes: 6 additions & 4 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use bevy_render::{
Extract,
};
use bevy_transform::{components::GlobalTransform, prelude::Transform};
use bevy_utils::FloatOrd;
use bevy_utils::{
tracing::{error, warn},
HashMap,
Expand Down Expand Up @@ -1653,7 +1652,7 @@ pub struct Shadow {
}

impl PhaseItem for Shadow {
type SortKey = FloatOrd;
type SortKey = usize;

#[inline]
fn entity(&self) -> Entity {
Expand All @@ -1662,7 +1661,7 @@ impl PhaseItem for Shadow {

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
self.pipeline.id()
}

#[inline]
Expand All @@ -1672,7 +1671,10 @@ impl PhaseItem for Shadow {

#[inline]
fn sort(items: &mut [Self]) {
radsort::sort_by_key(items, |item| item.distance);
// The shadow phase is sorted by pipeline id for performance reasons.
// Grouping all draw commands using the same pipeline together performs
// better than rebinding everything at a high rate.
radsort::sort_by_key(items, |item| item.pipeline.id());
}
}

Expand Down
10 changes: 10 additions & 0 deletions crates/bevy_render/src/render_resource/pipeline_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ pub struct CachedRenderPipelineId(CachedPipelineId);
impl CachedRenderPipelineId {
/// An invalid cached render pipeline index, often used to initialize a variable.
pub const INVALID: Self = CachedRenderPipelineId(usize::MAX);

#[inline]
pub fn id(&self) -> usize {
self.0
}
}

/// Index of a cached compute pipeline in a [`PipelineCache`].
Expand All @@ -65,6 +70,11 @@ pub struct CachedComputePipelineId(CachedPipelineId);
impl CachedComputePipelineId {
/// An invalid cached compute pipeline index, often used to initialize a variable.
pub const INVALID: Self = CachedComputePipelineId(usize::MAX);

#[inline]
pub fn id(&self) -> usize {
self.0
}
}

pub struct CachedPipeline {
Expand Down

0 comments on commit 13e47fe

Please sign in to comment.