Pack the StandardMaterialKey into a single scalar instead of a structure. (#12783)

This commit changes the `StandardMaterialKey` to be based on a set of
bitflags instead of a structure. We hash it every frame for every mesh,
and `#[derive(Hash)]` doesn't generate particularly efficient code for
large structures full of small types. Packing it into a single `u64`
therefore results in a roughly 10% speedup in `queue_material_meshes` on
`many_cubes --no-frustum-culling`.

![Screenshot 2024-03-29
075124](https://github.com/bevyengine/bevy/assets/157897/78afcab6-b616-489b-8243-da9a117f606c)
This commit is contained in:
Patrick Walton 2024-03-29 13:34:27 -05:00 committed by GitHub
parent fee824413f
commit 5b746d2b19
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,6 +5,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{ use bevy_render::{
mesh::MeshVertexBufferLayoutRef, render_asset::RenderAssets, render_resource::*, mesh::MeshVertexBufferLayoutRef, render_asset::RenderAssets, render_resource::*,
}; };
use bitflags::bitflags;
use crate::deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID; use crate::deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID;
use crate::*; use crate::*;
@ -751,30 +752,56 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
} }
} }
/// The pipeline key for [`StandardMaterial`]. bitflags! {
#[derive(Clone, PartialEq, Eq, Hash)] /// The pipeline key for `StandardMaterial`, packed into 64 bits.
pub struct StandardMaterialKey { #[derive(Clone, Copy, PartialEq, Eq, Hash)]
normal_map: bool, pub struct StandardMaterialKey: u64 {
cull_mode: Option<Face>, const CULL_FRONT = 0x01;
depth_bias: i32, const CULL_BACK = 0x02;
relief_mapping: bool, const NORMAL_MAP = 0x04;
diffuse_transmission: bool, const RELIEF_MAPPING = 0x08;
specular_transmission: bool, const DIFFUSE_TRANSMISSION = 0x10;
const SPECULAR_TRANSMISSION = 0x20;
const DEPTH_BIAS = 0xffffffff_00000000;
}
} }
const STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT: u64 = 32;
impl From<&StandardMaterial> for StandardMaterialKey { impl From<&StandardMaterial> for StandardMaterialKey {
fn from(material: &StandardMaterial) -> Self { fn from(material: &StandardMaterial) -> Self {
StandardMaterialKey { let mut key = StandardMaterialKey::empty();
normal_map: material.normal_map_texture.is_some(), key.set(
cull_mode: material.cull_mode, StandardMaterialKey::CULL_FRONT,
depth_bias: material.depth_bias as i32, material.cull_mode == Some(Face::Front),
relief_mapping: matches!( );
key.set(
StandardMaterialKey::CULL_BACK,
material.cull_mode == Some(Face::Back),
);
key.set(
StandardMaterialKey::NORMAL_MAP,
material.normal_map_texture.is_some(),
);
key.set(
StandardMaterialKey::RELIEF_MAPPING,
matches!(
material.parallax_mapping_method, material.parallax_mapping_method,
ParallaxMappingMethod::Relief { .. } ParallaxMappingMethod::Relief { .. }
), ),
diffuse_transmission: material.diffuse_transmission > 0.0, );
specular_transmission: material.specular_transmission > 0.0, key.set(
} StandardMaterialKey::DIFFUSE_TRANSMISSION,
material.diffuse_transmission > 0.0,
);
key.set(
StandardMaterialKey::SPECULAR_TRANSMISSION,
material.specular_transmission > 0.0,
);
key.insert(StandardMaterialKey::from_bits_retain(
(material.depth_bias as u64) << STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT,
));
key
} }
} }
@ -847,32 +874,58 @@ impl Material for StandardMaterial {
if let Some(fragment) = descriptor.fragment.as_mut() { if let Some(fragment) = descriptor.fragment.as_mut() {
let shader_defs = &mut fragment.shader_defs; let shader_defs = &mut fragment.shader_defs;
if key.bind_group_data.normal_map { if key
.bind_group_data
.contains(StandardMaterialKey::NORMAL_MAP)
{
shader_defs.push("STANDARD_MATERIAL_NORMAL_MAP".into()); shader_defs.push("STANDARD_MATERIAL_NORMAL_MAP".into());
} }
if key.bind_group_data.relief_mapping { if key
.bind_group_data
.contains(StandardMaterialKey::RELIEF_MAPPING)
{
shader_defs.push("RELIEF_MAPPING".into()); shader_defs.push("RELIEF_MAPPING".into());
} }
if key.bind_group_data.diffuse_transmission { if key
.bind_group_data
.contains(StandardMaterialKey::DIFFUSE_TRANSMISSION)
{
shader_defs.push("STANDARD_MATERIAL_DIFFUSE_TRANSMISSION".into()); shader_defs.push("STANDARD_MATERIAL_DIFFUSE_TRANSMISSION".into());
} }
if key.bind_group_data.specular_transmission { if key
.bind_group_data
.contains(StandardMaterialKey::SPECULAR_TRANSMISSION)
{
shader_defs.push("STANDARD_MATERIAL_SPECULAR_TRANSMISSION".into()); shader_defs.push("STANDARD_MATERIAL_SPECULAR_TRANSMISSION".into());
} }
if key.bind_group_data.diffuse_transmission || key.bind_group_data.specular_transmission if key.bind_group_data.intersects(
{ StandardMaterialKey::DIFFUSE_TRANSMISSION
| StandardMaterialKey::SPECULAR_TRANSMISSION,
) {
shader_defs.push("STANDARD_MATERIAL_SPECULAR_OR_DIFFUSE_TRANSMISSION".into()); shader_defs.push("STANDARD_MATERIAL_SPECULAR_OR_DIFFUSE_TRANSMISSION".into());
} }
} }
descriptor.primitive.cull_mode = key.bind_group_data.cull_mode;
descriptor.primitive.cull_mode = if key
.bind_group_data
.contains(StandardMaterialKey::CULL_FRONT)
{
Some(Face::Front)
} else if key.bind_group_data.contains(StandardMaterialKey::CULL_BACK) {
Some(Face::Back)
} else {
None
};
if let Some(label) = &mut descriptor.label { if let Some(label) = &mut descriptor.label {
*label = format!("pbr_{}", *label).into(); *label = format!("pbr_{}", *label).into();
} }
if let Some(depth_stencil) = descriptor.depth_stencil.as_mut() { if let Some(depth_stencil) = descriptor.depth_stencil.as_mut() {
depth_stencil.bias.constant = key.bind_group_data.depth_bias; depth_stencil.bias.constant =
(key.bind_group_data.bits() >> STANDARD_MATERIAL_KEY_DEPTH_BIAS_SHIFT) as i32;
} }
Ok(()) Ok(())
} }