sort alpha masked pipelines by pipeline & mesh instead of by distance (#12117)

# Objective

- followup to https://github.com/bevyengine/bevy/pull/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.
This commit is contained in:
Elabajaba 2024-02-26 06:14:59 -05:00 committed by GitHub
parent a463d0c637
commit 78b6fa1f1b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 31 additions and 39 deletions

View file

@ -38,7 +38,7 @@ pub mod graph {
// PERF: vulkan docs recommend using 24 bit depth for better performance
pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float;
use std::{cmp::Reverse, ops::Range};
use std::ops::Range;
use bevy_asset::AssetId;
pub use camera_3d::*;
@ -242,7 +242,7 @@ impl CachedRenderPipelinePhaseItem for Opaque3d {
}
pub struct AlphaMask3d {
pub distance: f32,
pub asset_id: AssetId<Mesh>,
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
@ -251,8 +251,7 @@ pub struct AlphaMask3d {
}
impl PhaseItem for AlphaMask3d {
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (usize, AssetId<Mesh>);
#[inline]
fn entity(&self) -> Entity {
@ -261,7 +260,8 @@ impl PhaseItem for AlphaMask3d {
#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
(self.pipeline.id(), self.asset_id)
}
#[inline]
@ -271,8 +271,7 @@ impl PhaseItem for AlphaMask3d {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
items.sort_unstable_by_key(Self::sort_key);
}
#[inline]

View file

@ -1,7 +1,7 @@
pub mod copy_lighting_id;
pub mod node;
use std::{cmp::Reverse, ops::Range};
use std::ops::Range;
use bevy_asset::AssetId;
use bevy_ecs::prelude::*;
@ -10,7 +10,7 @@ use bevy_render::{
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
render_resource::{CachedRenderPipelineId, TextureFormat},
};
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
use bevy_utils::nonmax::NonMaxU32;
pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint;
pub const DEFERRED_LIGHTING_PASS_ID_FORMAT: TextureFormat = TextureFormat::R8Uint;
@ -18,7 +18,7 @@ pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat:
/// Opaque phase of the 3D Deferred pass.
///
/// Sorted front-to-back by the z-distance in front of the camera.
/// Sorted by pipeline, then by mesh to improve batching.
///
/// Used to render all 3D meshes with materials that have no transparency.
pub struct Opaque3dDeferred {
@ -84,11 +84,11 @@ impl CachedRenderPipelinePhaseItem for Opaque3dDeferred {
/// Alpha mask phase of the 3D Deferred pass.
///
/// Sorted front-to-back by the z-distance in front of the camera.
/// Sorted by pipeline, then by mesh to improve batching.
///
/// Used to render all meshes with a material with an alpha mask.
pub struct AlphaMask3dDeferred {
pub distance: f32,
pub asset_id: AssetId<Mesh>,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
@ -97,8 +97,7 @@ pub struct AlphaMask3dDeferred {
}
impl PhaseItem for AlphaMask3dDeferred {
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (usize, AssetId<Mesh>);
#[inline]
fn entity(&self) -> Entity {
@ -107,7 +106,8 @@ impl PhaseItem for AlphaMask3dDeferred {
#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
(self.pipeline_id.id(), self.asset_id)
}
#[inline]
@ -117,8 +117,7 @@ impl PhaseItem for AlphaMask3dDeferred {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
items.sort_unstable_by_key(Self::sort_key);
}
#[inline]

View file

@ -27,7 +27,7 @@
pub mod node;
use std::{cmp::Reverse, ops::Range};
use std::ops::Range;
use bevy_asset::AssetId;
use bevy_ecs::prelude::*;
@ -38,7 +38,7 @@ use bevy_render::{
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
texture::ColorAttachment,
};
use bevy_utils::{nonmax::NonMaxU32, FloatOrd};
use bevy_utils::nonmax::NonMaxU32;
pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
@ -107,7 +107,7 @@ impl ViewPrepassTextures {
/// Opaque phase of the 3D prepass.
///
/// Sorted front-to-back by the z-distance in front of the camera.
/// Sorted by pipeline, then by mesh to improve batching.
///
/// Used to render all 3D meshes with materials that have no transparency.
pub struct Opaque3dPrepass {
@ -173,11 +173,11 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
/// Alpha mask phase of the 3D prepass.
///
/// Sorted front-to-back by the z-distance in front of the camera.
/// Sorted by pipeline, then by mesh to improve batching.
///
/// Used to render all meshes with a material with an alpha mask.
pub struct AlphaMask3dPrepass {
pub distance: f32,
pub asset_id: AssetId<Mesh>,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
@ -186,8 +186,7 @@ pub struct AlphaMask3dPrepass {
}
impl PhaseItem for AlphaMask3dPrepass {
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = Reverse<FloatOrd>;
type SortKey = (usize, AssetId<Mesh>);
#[inline]
fn entity(&self) -> Entity {
@ -196,7 +195,8 @@ impl PhaseItem for AlphaMask3dPrepass {
#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
(self.pipeline_id.id(), self.asset_id)
}
#[inline]
@ -206,8 +206,7 @@ impl PhaseItem for AlphaMask3dPrepass {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| -item.distance);
items.sort_unstable_by_key(Self::sort_key);
}
#[inline]

View file

@ -672,10 +672,10 @@ pub fn queue_material_meshes<M: Material>(
}
}
AlphaMode::Mask(_) => {
if material.properties.reads_view_transmission_texture {
let distance = rangefinder
.distance_translation(&mesh_instance.transforms.transform.translation)
+ material.properties.depth_bias;
if material.properties.reads_view_transmission_texture {
transmissive_phase.add(Transmissive3d {
entity: *visible_entity,
draw_function: draw_transmissive_pbr,
@ -689,7 +689,7 @@ pub fn queue_material_meshes<M: Material>(
entity: *visible_entity,
draw_function: draw_alpha_mask_pbr,
pipeline: pipeline_id,
distance,
asset_id: mesh_instance.mesh_asset_id,
batch_range: 0..1,
dynamic_offset: None,
});

View file

@ -733,7 +733,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
.get_id::<DrawPrepass<M>>()
.unwrap();
for (
view,
_view,
visible_entities,
mut opaque_phase,
mut alpha_mask_phase,
@ -756,8 +756,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}
let rangefinder = view.rangefinder3d();
for visible_entity in &visible_entities.entities {
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
continue;
@ -860,9 +858,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
}
}
AlphaMode::Mask(_) => {
let distance = rangefinder
.distance_translation(&mesh_instance.transforms.transform.translation)
+ material.properties.depth_bias;
if deferred {
alpha_mask_deferred_phase
.as_mut()
@ -871,7 +866,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
entity: *visible_entity,
draw_function: alpha_mask_draw_deferred,
pipeline_id,
distance,
asset_id: mesh_instance.mesh_asset_id,
batch_range: 0..1,
dynamic_offset: None,
});
@ -880,7 +875,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
entity: *visible_entity,
draw_function: alpha_mask_draw_prepass,
pipeline_id,
distance,
asset_id: mesh_instance.mesh_asset_id,
batch_range: 0..1,
dynamic_offset: None,
});