Skip to content

Commit

Permalink
sort alpha masked pipelines by pipeline & mesh instead of by distance (
Browse files Browse the repository at this point in the history
…bevyengine#12117)

# Objective

- followup to bevyengine#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.
  • Loading branch information
Elabajaba authored and msvbg committed Feb 26, 2024
1 parent 9b81266 commit 6b993b5
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 39 deletions.
13 changes: 6 additions & 7 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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]
Expand All @@ -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]
Expand Down
19 changes: 9 additions & 10 deletions crates/bevy_core_pipeline/src/deferred/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -10,15 +10,15 @@ 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;
pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth16Unorm;

/// 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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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]
Expand All @@ -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]
Expand Down
19 changes: 9 additions & 10 deletions crates/bevy_core_pipeline/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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]
Expand All @@ -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]
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,10 +672,10 @@ pub fn queue_material_meshes<M: Material>(
}
}
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,
Expand All @@ -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,
});
Expand Down
11 changes: 3 additions & 8 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand Down

0 comments on commit 6b993b5

Please sign in to comment.