Skip to content

Commit 78b6fa1

Browse files
authored
sort alpha masked pipelines by pipeline & mesh instead of by distance (#12117)
# Objective - followup to #11671 - I forgot to change the alpha masked phases. ## Solution - Change the sorting for alpha mask phases to sort by pipeline+mesh instead of distance, for much better batching for alpha masked materials. I also fixed some docs that I missed in the previous PR. --- ## Changelog - Alpha masked materials are now sorted by pipeline and mesh.
1 parent a463d0c commit 78b6fa1

File tree

5 files changed

+31
-39
lines changed

5 files changed

+31
-39
lines changed

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub mod graph {
3838
// PERF: vulkan docs recommend using 24 bit depth for better performance
3939
pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
4040

41-
use std::{cmp::Reverse, ops::Range};
41+
use std::ops::Range;
4242

4343
use bevy_asset::AssetId;
4444
pub use camera_3d::*;
@@ -242,7 +242,7 @@ impl CachedRenderPipelinePhaseItem for Opaque3d {
242242
}
243243

244244
pub struct AlphaMask3d {
245-
pub distance: f32,
245+
pub asset_id: AssetId<Mesh>,
246246
pub pipeline: CachedRenderPipelineId,
247247
pub entity: Entity,
248248
pub draw_function: DrawFunctionId,
@@ -251,8 +251,7 @@ pub struct AlphaMask3d {
251251
}
252252

253253
impl PhaseItem for AlphaMask3d {
254-
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
255-
type SortKey = Reverse<FloatOrd>;
254+
type SortKey = (usize, AssetId<Mesh>);
256255

257256
#[inline]
258257
fn entity(&self) -> Entity {
@@ -261,7 +260,8 @@ impl PhaseItem for AlphaMask3d {
261260

262261
#[inline]
263262
fn sort_key(&self) -> Self::SortKey {
264-
Reverse(FloatOrd(self.distance))
263+
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
264+
(self.pipeline.id(), self.asset_id)
265265
}
266266

267267
#[inline]
@@ -271,8 +271,7 @@ impl PhaseItem for AlphaMask3d {
271271

272272
#[inline]
273273
fn sort(items: &mut [Self]) {
274-
// Key negated to match reversed SortKey ordering
275-
radsort::sort_by_key(items, |item| -item.distance);
274+
items.sort_unstable_by_key(Self::sort_key);
276275
}
277276

278277
#[inline]

crates/bevy_core_pipeline/src/deferred/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub mod copy_lighting_id;
22
pub mod node;
33

4-
use std::{cmp::Reverse, ops::Range};
4+
use std::ops::Range;
55

66
use bevy_asset::AssetId;
77
use bevy_ecs::prelude::*;
@@ -10,15 +10,15 @@ use bevy_render::{
1010
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
1111
render_resource::{CachedRenderPipelineId, TextureFormat},
1212
};
13-
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
13+
use bevy_utils::nonmax::NonMaxU32;
1414

1515
pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint;
1616
pub const DEFERRED_LIGHTING_PASS_ID_FORMAT: TextureFormat = TextureFormat::R8Uint;
1717
pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth16Unorm;
1818

1919
/// Opaque phase of the 3D Deferred pass.
2020
///
21-
/// Sorted front-to-back by the z-distance in front of the camera.
21+
/// Sorted by pipeline, then by mesh to improve batching.
2222
///
2323
/// Used to render all 3D meshes with materials that have no transparency.
2424
pub struct Opaque3dDeferred {
@@ -84,11 +84,11 @@ impl CachedRenderPipelinePhaseItem for Opaque3dDeferred {
8484

8585
/// Alpha mask phase of the 3D Deferred pass.
8686
///
87-
/// Sorted front-to-back by the z-distance in front of the camera.
87+
/// Sorted by pipeline, then by mesh to improve batching.
8888
///
8989
/// Used to render all meshes with a material with an alpha mask.
9090
pub struct AlphaMask3dDeferred {
91-
pub distance: f32,
91+
pub asset_id: AssetId<Mesh>,
9292
pub entity: Entity,
9393
pub pipeline_id: CachedRenderPipelineId,
9494
pub draw_function: DrawFunctionId,
@@ -97,8 +97,7 @@ pub struct AlphaMask3dDeferred {
9797
}
9898

9999
impl PhaseItem for AlphaMask3dDeferred {
100-
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
101-
type SortKey = Reverse<FloatOrd>;
100+
type SortKey = (usize, AssetId<Mesh>);
102101

103102
#[inline]
104103
fn entity(&self) -> Entity {
@@ -107,7 +106,8 @@ impl PhaseItem for AlphaMask3dDeferred {
107106

108107
#[inline]
109108
fn sort_key(&self) -> Self::SortKey {
110-
Reverse(FloatOrd(self.distance))
109+
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
110+
(self.pipeline_id.id(), self.asset_id)
111111
}
112112

113113
#[inline]
@@ -117,8 +117,7 @@ impl PhaseItem for AlphaMask3dDeferred {
117117

118118
#[inline]
119119
fn sort(items: &mut [Self]) {
120-
// Key negated to match reversed SortKey ordering
121-
radsort::sort_by_key(items, |item| -item.distance);
120+
items.sort_unstable_by_key(Self::sort_key);
122121
}
123122

124123
#[inline]

crates/bevy_core_pipeline/src/prepass/mod.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
2828
pub mod node;
2929

30-
use std::{cmp::Reverse, ops::Range};
30+
use std::ops::Range;
3131

3232
use bevy_asset::AssetId;
3333
use bevy_ecs::prelude::*;
@@ -38,7 +38,7 @@ use bevy_render::{
3838
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
3939
texture::ColorAttachment,
4040
};
41-
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
41+
use bevy_utils::nonmax::NonMaxU32;
4242

4343
pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
4444
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
@@ -107,7 +107,7 @@ impl ViewPrepassTextures {
107107

108108
/// Opaque phase of the 3D prepass.
109109
///
110-
/// Sorted front-to-back by the z-distance in front of the camera.
110+
/// Sorted by pipeline, then by mesh to improve batching.
111111
///
112112
/// Used to render all 3D meshes with materials that have no transparency.
113113
pub struct Opaque3dPrepass {
@@ -173,11 +173,11 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
173173

174174
/// Alpha mask phase of the 3D prepass.
175175
///
176-
/// Sorted front-to-back by the z-distance in front of the camera.
176+
/// Sorted by pipeline, then by mesh to improve batching.
177177
///
178178
/// Used to render all meshes with a material with an alpha mask.
179179
pub struct AlphaMask3dPrepass {
180-
pub distance: f32,
180+
pub asset_id: AssetId<Mesh>,
181181
pub entity: Entity,
182182
pub pipeline_id: CachedRenderPipelineId,
183183
pub draw_function: DrawFunctionId,
@@ -186,8 +186,7 @@ pub struct AlphaMask3dPrepass {
186186
}
187187

188188
impl PhaseItem for AlphaMask3dPrepass {
189-
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
190-
type SortKey = Reverse<FloatOrd>;
189+
type SortKey = (usize, AssetId<Mesh>);
191190

192191
#[inline]
193192
fn entity(&self) -> Entity {
@@ -196,7 +195,8 @@ impl PhaseItem for AlphaMask3dPrepass {
196195

197196
#[inline]
198197
fn sort_key(&self) -> Self::SortKey {
199-
Reverse(FloatOrd(self.distance))
198+
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
199+
(self.pipeline_id.id(), self.asset_id)
200200
}
201201

202202
#[inline]
@@ -206,8 +206,7 @@ impl PhaseItem for AlphaMask3dPrepass {
206206

207207
#[inline]
208208
fn sort(items: &mut [Self]) {
209-
// Key negated to match reversed SortKey ordering
210-
radsort::sort_by_key(items, |item| -item.distance);
209+
items.sort_unstable_by_key(Self::sort_key);
211210
}
212211

213212
#[inline]

crates/bevy_pbr/src/material.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -672,10 +672,10 @@ pub fn queue_material_meshes<M: Material>(
672672
}
673673
}
674674
AlphaMode::Mask(_) => {
675-
let distance = rangefinder
676-
.distance_translation(&mesh_instance.transforms.transform.translation)
677-
+ material.properties.depth_bias;
678675
if material.properties.reads_view_transmission_texture {
676+
let distance = rangefinder
677+
.distance_translation(&mesh_instance.transforms.transform.translation)
678+
+ material.properties.depth_bias;
679679
transmissive_phase.add(Transmissive3d {
680680
entity: *visible_entity,
681681
draw_function: draw_transmissive_pbr,
@@ -689,7 +689,7 @@ pub fn queue_material_meshes<M: Material>(
689689
entity: *visible_entity,
690690
draw_function: draw_alpha_mask_pbr,
691691
pipeline: pipeline_id,
692-
distance,
692+
asset_id: mesh_instance.mesh_asset_id,
693693
batch_range: 0..1,
694694
dynamic_offset: None,
695695
});

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
733733
.get_id::<DrawPrepass<M>>()
734734
.unwrap();
735735
for (
736-
view,
736+
_view,
737737
visible_entities,
738738
mut opaque_phase,
739739
mut alpha_mask_phase,
@@ -756,8 +756,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
756756
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
757757
}
758758

759-
let rangefinder = view.rangefinder3d();
760-
761759
for visible_entity in &visible_entities.entities {
762760
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
763761
continue;
@@ -860,9 +858,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
860858
}
861859
}
862860
AlphaMode::Mask(_) => {
863-
let distance = rangefinder
864-
.distance_translation(&mesh_instance.transforms.transform.translation)
865-
+ material.properties.depth_bias;
866861
if deferred {
867862
alpha_mask_deferred_phase
868863
.as_mut()
@@ -871,7 +866,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
871866
entity: *visible_entity,
872867
draw_function: alpha_mask_draw_deferred,
873868
pipeline_id,
874-
distance,
869+
asset_id: mesh_instance.mesh_asset_id,
875870
batch_range: 0..1,
876871
dynamic_offset: None,
877872
});
@@ -880,7 +875,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
880875
entity: *visible_entity,
881876
draw_function: alpha_mask_draw_prepass,
882877
pipeline_id,
883-
distance,
878+
asset_id: mesh_instance.mesh_asset_id,
884879
batch_range: 0..1,
885880
dynamic_offset: None,
886881
});

0 commit comments

Comments
 (0)