From f88251aabaaf6a30283301687c2787050e558a4b Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sat, 3 Feb 2024 00:47:51 -0500 Subject: [PATCH 1/6] sort by pipeline then mesh for opaque3d for massively better batching --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 13 +++++++------ crates/bevy_pbr/src/material.rs | 14 ++++++++++---- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 9e542d6c3240a..60c83a9a8d8dd 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -40,6 +40,7 @@ pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float; use std::{cmp::Reverse, ops::Range}; +use bevy_asset::AssetId; pub use camera_3d::*; pub use main_opaque_pass_3d_node::*; pub use main_transparent_pass_3d_node::*; @@ -50,6 +51,7 @@ use bevy_render::{ camera::{Camera, ExtractedCamera}, color::Color, extract_component::ExtractComponentPlugin, + mesh::Mesh, prelude::Msaa, render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner}, render_phase::{ @@ -182,7 +184,7 @@ impl Plugin for Core3dPlugin { } pub struct Opaque3d { - pub distance: f32, + pub asset_id: AssetId, pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, @@ -191,8 +193,7 @@ pub struct Opaque3d { } impl PhaseItem for Opaque3d { - // 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 { @@ -201,7 +202,8 @@ impl PhaseItem for Opaque3d { #[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] @@ -211,8 +213,7 @@ impl PhaseItem for Opaque3d { #[inline] fn sort(items: &mut [Self]) { - // Key negated to match reversed SortKey ordering - radsort::sort_by_key(items, |item| -item.distance); + items.sort_by_key(Self::sort_key) } #[inline] diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 8e2b3bd71d91b..c9b3b5ca578f2 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -639,12 +639,12 @@ pub fn queue_material_meshes( mesh_instance.material_bind_group_id = material.get_bind_group_id(); - let distance = rangefinder - .distance_translation(&mesh_instance.transforms.transform.translation) - + material.properties.depth_bias; match material.properties.alpha_mode { AlphaMode::Opaque => { 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, @@ -658,13 +658,16 @@ pub fn queue_material_meshes( entity: *visible_entity, draw_function: draw_opaque_pbr, pipeline: pipeline_id, - distance, + asset_id: mesh_instance.mesh_asset_id, batch_range: 0..1, dynamic_offset: None, }); } } AlphaMode::Mask(_) => { + 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, @@ -689,6 +692,9 @@ pub fn queue_material_meshes( | AlphaMode::Premultiplied | AlphaMode::Add | AlphaMode::Multiply => { + let distance = rangefinder + .distance_translation(&mesh_instance.transforms.transform.translation) + + material.properties.depth_bias; transparent_phase.add(Transparent3d { entity: *visible_entity, draw_function: draw_transparent_pbr, From 4269d61cb25bffcdc9789145dfa5e9a2bc654bee Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sat, 3 Feb 2024 01:26:13 -0500 Subject: [PATCH 2/6] deferred and prepass --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 2 +- crates/bevy_core_pipeline/src/deferred/mod.rs | 13 +++++++------ crates/bevy_core_pipeline/src/prepass/mod.rs | 12 +++++++----- crates/bevy_pbr/src/prepass/mod.rs | 10 +++++----- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 60c83a9a8d8dd..279e482ed8810 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -213,7 +213,7 @@ impl PhaseItem for Opaque3d { #[inline] fn sort(items: &mut [Self]) { - items.sort_by_key(Self::sort_key) + 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 a8a56e39a1163..0afb588e3bb0f 100644 --- a/crates/bevy_core_pipeline/src/deferred/mod.rs +++ b/crates/bevy_core_pipeline/src/deferred/mod.rs @@ -3,8 +3,10 @@ pub mod node; use std::{cmp::Reverse, ops::Range}; +use bevy_asset::AssetId; use bevy_ecs::prelude::*; use bevy_render::{ + mesh::Mesh, render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem}, render_resource::{CachedRenderPipelineId, TextureFormat}, }; @@ -20,8 +22,8 @@ pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat: /// /// Used to render all 3D meshes with materials that have no transparency. pub struct Opaque3dDeferred { - pub distance: f32, pub entity: Entity, + pub asset_id: AssetId, pub pipeline_id: CachedRenderPipelineId, pub draw_function: DrawFunctionId, pub batch_range: Range, @@ -29,8 +31,7 @@ pub struct Opaque3dDeferred { } impl PhaseItem for Opaque3dDeferred { - // 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 { @@ -39,7 +40,8 @@ impl PhaseItem for Opaque3dDeferred { #[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] @@ -49,8 +51,7 @@ impl PhaseItem for Opaque3dDeferred { #[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 c8d38db50f9fd..0a5dbd85d3b56 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -29,9 +29,11 @@ pub mod node; use std::{cmp::Reverse, ops::Range}; +use bevy_asset::AssetId; use bevy_ecs::prelude::*; use bevy_reflect::Reflect; use bevy_render::{ + mesh::Mesh, render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem}, render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView}, texture::ColorAttachment, @@ -109,8 +111,8 @@ impl ViewPrepassTextures { /// /// Used to render all 3D meshes with materials that have no transparency. pub struct Opaque3dPrepass { - pub distance: f32, pub entity: Entity, + pub asset_id: AssetId, pub pipeline_id: CachedRenderPipelineId, pub draw_function: DrawFunctionId, pub batch_range: Range, @@ -118,8 +120,7 @@ pub struct Opaque3dPrepass { } impl PhaseItem for Opaque3dPrepass { - // 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 { @@ -128,7 +129,8 @@ impl PhaseItem for Opaque3dPrepass { #[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] @@ -139,7 +141,7 @@ impl PhaseItem for Opaque3dPrepass { #[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/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 6c8eef1bf60ab..00380acdc84ac 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -835,9 +835,6 @@ pub fn queue_prepass_material_meshes( } }; - let distance = rangefinder - .distance_translation(&mesh_instance.transforms.transform.translation) - + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { if deferred { @@ -848,7 +845,7 @@ pub fn queue_prepass_material_meshes( entity: *visible_entity, draw_function: opaque_draw_deferred, pipeline_id, - distance, + asset_id: mesh_instance.mesh_asset_id, batch_range: 0..1, dynamic_offset: None, }); @@ -857,13 +854,16 @@ pub fn queue_prepass_material_meshes( entity: *visible_entity, draw_function: opaque_draw_prepass, pipeline_id, - distance, + asset_id: mesh_instance.mesh_asset_id, batch_range: 0..1, dynamic_offset: None, }); } } AlphaMode::Mask(_) => { + let distance = rangefinder + .distance_translation(&mesh_instance.transforms.transform.translation) + + material.properties.depth_bias; if deferred { alpha_mask_deferred_phase .as_mut() From aff93f756e30e9ce022c40dac0af97e661febf9c Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sat, 3 Feb 2024 01:52:23 -0500 Subject: [PATCH 3/6] ci and doc --- .../bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs | 2 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 2 +- crates/bevy_core_pipeline/src/deferred/mod.rs | 2 +- crates/bevy_core_pipeline/src/prepass/mod.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index 804f6afcf8e18..856040e3cf442 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -47,7 +47,7 @@ impl ViewNode for MainOpaquePass3dNode { ): QueryItem, world: &World, ) -> Result<(), NodeRunError> { - // Run the opaque pass, sorted front-to-back + // Run the opaque pass, sorted by pipeline key and mesh id to greatly improve batching. // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered(); diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 279e482ed8810..eaeedac2c31c5 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -213,7 +213,7 @@ impl PhaseItem for Opaque3d { #[inline] fn sort(items: &mut [Self]) { - items.sort_unstable_by_key(Self::sort_key) + 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 0afb588e3bb0f..bd5dfed704c08 100644 --- a/crates/bevy_core_pipeline/src/deferred/mod.rs +++ b/crates/bevy_core_pipeline/src/deferred/mod.rs @@ -51,7 +51,7 @@ impl PhaseItem for Opaque3dDeferred { #[inline] fn sort(items: &mut [Self]) { - items.sort_unstable_by_key(Self::sort_key) + 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 0a5dbd85d3b56..43765336b1463 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -140,8 +140,7 @@ impl PhaseItem for Opaque3dPrepass { #[inline] fn sort(items: &mut [Self]) { - // Key negated to match reversed SortKey ordering - items.sort_unstable_by_key(Self::sort_key) + items.sort_unstable_by_key(Self::sort_key); } #[inline] From 992ff4f6c2c93a31fee49890dbe1410bc4e7006b Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sat, 3 Feb 2024 03:27:03 -0500 Subject: [PATCH 4/6] sort opaque3d by bindgroupid as well --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 9 +++++---- crates/bevy_pbr/src/material.rs | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index eaeedac2c31c5..816a52c7e4a4d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -59,8 +59,8 @@ use bevy_render::{ RenderPhase, }, render_resource::{ - CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor, Texture, - TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, + BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor, + Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, }, renderer::RenderDevice, texture::{BevyDefault, ColorAttachment, TextureCache}, @@ -185,6 +185,7 @@ impl Plugin for Core3dPlugin { pub struct Opaque3d { pub asset_id: AssetId, + pub bind_group_id: BindGroupId, pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, @@ -193,7 +194,7 @@ pub struct Opaque3d { } impl PhaseItem for Opaque3d { - type SortKey = (usize, AssetId); + type SortKey = (usize, BindGroupId, AssetId); #[inline] fn entity(&self) -> Entity { @@ -203,7 +204,7 @@ impl PhaseItem for Opaque3d { #[inline] fn sort_key(&self) -> Self::SortKey { // Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes. - (self.pipeline.id(), self.asset_id) + (self.pipeline.id(), self.bind_group_id, self.asset_id) } #[inline] diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index c9b3b5ca578f2..75358bc281302 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -659,6 +659,8 @@ pub fn queue_material_meshes( draw_function: draw_opaque_pbr, pipeline: pipeline_id, asset_id: mesh_instance.mesh_asset_id, + // TODO: Remove this unwrap somehow. Sorting by the bind_group_id gets us another 9% fps in my testing + bind_group_id: mesh_instance.material_bind_group_id.0.unwrap(), batch_range: 0..1, dynamic_offset: None, }); From c8494ce1544808d80df5a2fba5c30729f187d346 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sat, 3 Feb 2024 03:40:23 -0500 Subject: [PATCH 5/6] forgot the part that lets us sort the bindgroupid --- crates/bevy_render/src/render_resource/resource_macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/resource_macros.rs b/crates/bevy_render/src/render_resource/resource_macros.rs index ab5ce2cbebe19..441b922d38675 100644 --- a/crates/bevy_render/src/render_resource/resource_macros.rs +++ b/crates/bevy_render/src/render_resource/resource_macros.rs @@ -116,7 +116,7 @@ macro_rules! render_resource_wrapper { #[macro_export] macro_rules! define_atomic_id { ($atomic_id_type:ident) => { - #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] + #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] pub struct $atomic_id_type(core::num::NonZeroU32); // We use new instead of default to indicate that each ID created will be unique. From 264e5336c6de9ef60e1c72e3ecde4d13ca2e6d42 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Sun, 4 Feb 2024 17:37:08 -0500 Subject: [PATCH 6/6] revert sorting by bind group as I don't have time to fix the unwrap --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 9 ++++----- crates/bevy_pbr/src/material.rs | 2 -- .../bevy_render/src/render_resource/resource_macros.rs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 816a52c7e4a4d..eaeedac2c31c5 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -59,8 +59,8 @@ use bevy_render::{ RenderPhase, }, render_resource::{ - BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor, - Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, + CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor, Texture, + TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, }, renderer::RenderDevice, texture::{BevyDefault, ColorAttachment, TextureCache}, @@ -185,7 +185,6 @@ impl Plugin for Core3dPlugin { pub struct Opaque3d { pub asset_id: AssetId, - pub bind_group_id: BindGroupId, pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, @@ -194,7 +193,7 @@ pub struct Opaque3d { } impl PhaseItem for Opaque3d { - type SortKey = (usize, BindGroupId, AssetId); + type SortKey = (usize, AssetId); #[inline] fn entity(&self) -> Entity { @@ -204,7 +203,7 @@ impl PhaseItem for Opaque3d { #[inline] fn sort_key(&self) -> Self::SortKey { // Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes. - (self.pipeline.id(), self.bind_group_id, self.asset_id) + (self.pipeline.id(), self.asset_id) } #[inline] diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 75358bc281302..c9b3b5ca578f2 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -659,8 +659,6 @@ pub fn queue_material_meshes( draw_function: draw_opaque_pbr, pipeline: pipeline_id, asset_id: mesh_instance.mesh_asset_id, - // TODO: Remove this unwrap somehow. Sorting by the bind_group_id gets us another 9% fps in my testing - bind_group_id: mesh_instance.material_bind_group_id.0.unwrap(), batch_range: 0..1, dynamic_offset: None, }); diff --git a/crates/bevy_render/src/render_resource/resource_macros.rs b/crates/bevy_render/src/render_resource/resource_macros.rs index 441b922d38675..ab5ce2cbebe19 100644 --- a/crates/bevy_render/src/render_resource/resource_macros.rs +++ b/crates/bevy_render/src/render_resource/resource_macros.rs @@ -116,7 +116,7 @@ macro_rules! render_resource_wrapper { #[macro_export] macro_rules! define_atomic_id { ($atomic_id_type:ident) => { - #[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)] + #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct $atomic_id_type(core::num::NonZeroU32); // We use new instead of default to indicate that each ID created will be unique.