From 78b6fa1f1b883d6c8e4d2f671cc64e8b300a67e6 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Mon, 26 Feb 2024 06:14:59 -0500 Subject: [PATCH] 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. --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 13 ++++++------- crates/bevy_core_pipeline/src/deferred/mod.rs | 19 +++++++++---------- crates/bevy_core_pipeline/src/prepass/mod.rs | 19 +++++++++---------- crates/bevy_pbr/src/material.rs | 8 ++++---- crates/bevy_pbr/src/prepass/mod.rs | 11 +++-------- 5 files changed, 31 insertions(+), 39 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 9c049683eb..b4d407776d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -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, 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; + type SortKey = (usize, AssetId); #[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] diff --git a/crates/bevy_core_pipeline/src/deferred/mod.rs b/crates/bevy_core_pipeline/src/deferred/mod.rs index bd5dfed704..fd8ca0966d 100644 --- a/crates/bevy_core_pipeline/src/deferred/mod.rs +++ b/crates/bevy_core_pipeline/src/deferred/mod.rs @@ -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, 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; + type SortKey = (usize, AssetId); #[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] diff --git a/crates/bevy_core_pipeline/src/prepass/mod.rs b/crates/bevy_core_pipeline/src/prepass/mod.rs index 43765336b1..c250985ffc 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -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, 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; + type SortKey = (usize, AssetId); #[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] diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 57770c0448..9fe9abd1a8 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -672,10 +672,10 @@ pub fn queue_material_meshes( } } AlphaMode::Mask(_) => { - let distance = rangefinder - .distance_translation(&mesh_instance.transforms.transform.translation) - + material.properties.depth_bias; if material.properties.reads_view_transmission_texture { + let distance = rangefinder + .distance_translation(&mesh_instance.transforms.transform.translation) + + material.properties.depth_bias; transmissive_phase.add(Transmissive3d { entity: *visible_entity, draw_function: draw_transmissive_pbr, @@ -689,7 +689,7 @@ pub fn queue_material_meshes( 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, }); diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 6f44515341..b0b17b4a03 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -733,7 +733,7 @@ pub fn queue_prepass_material_meshes( .get_id::>() .unwrap(); for ( - view, + _view, visible_entities, mut opaque_phase, mut alpha_mask_phase, @@ -756,8 +756,6 @@ pub fn queue_prepass_material_meshes( 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( } } 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( 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( 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, });