Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Micro-optimize queue_material_meshes, primarily to remove bit manipulation. #12791

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 22 additions & 23 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,25 +659,9 @@ pub fn queue_material_meshes<M: Material>(
continue;
};

let forward = match material.properties.render_method {
OpaqueRendererMethod::Forward => true,
OpaqueRendererMethod::Deferred => false,
OpaqueRendererMethod::Auto => unreachable!(),
};

let mut mesh_key = view_key;

mesh_key |= MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);

if mesh.morph_targets.is_some() {
mesh_key |= MeshPipelineKey::MORPH_TARGETS;
}

if material.properties.reads_view_transmission_texture {
mesh_key |= MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE;
}

mesh_key |= alpha_mode_pipeline_key(material.properties.alpha_mode);
let mut mesh_key = view_key
| MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
| material.properties.mesh_pipeline_key_bits;

if render_lightmaps
.render_lightmaps
Expand Down Expand Up @@ -721,7 +705,7 @@ pub fn queue_material_meshes<M: Material>(
batch_range: 0..1,
dynamic_offset: None,
});
} else if forward {
} else if material.properties.render_method == OpaqueRendererMethod::Forward {
opaque_phase.add(Opaque3d {
entity: *visible_entity,
draw_function: draw_opaque_pbr,
Expand All @@ -745,7 +729,7 @@ pub fn queue_material_meshes<M: Material>(
batch_range: 0..1,
dynamic_offset: None,
});
} else if forward {
} else if material.properties.render_method == OpaqueRendererMethod::Forward {
alpha_mask_phase.add(AlphaMask3d {
entity: *visible_entity,
draw_function: draw_alpha_mask_pbr,
Expand Down Expand Up @@ -817,7 +801,7 @@ impl DefaultOpaqueRendererMethod {
/// bandwidth usage which can be unsuitable for low end mobile or other bandwidth-constrained devices.
///
/// If a material indicates `OpaqueRendererMethod::Auto`, `DefaultOpaqueRendererMethod` will be used.
#[derive(Default, Clone, Copy, Debug, Reflect)]
#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)]
pub enum OpaqueRendererMethod {
#[default]
Forward,
Expand All @@ -832,6 +816,11 @@ pub struct MaterialProperties {
pub render_method: OpaqueRendererMethod,
/// The [`AlphaMode`] of this material.
pub alpha_mode: AlphaMode,
/// The bits in the [`MeshPipelineKey`] for this material.
///
/// These are precalculated so that we can just "or" them together in
/// [`queue_material_meshes`].
pub mesh_pipeline_key_bits: MeshPipelineKey,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this hard-tie the material system to meshes? Or should we generalize MeshPipelineKey?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would be a lot of untangling that would need to happen if we wanted to have meshes that didn't have materials, or materials that didn't have meshes. This doesn't really make things worse.

/// Add a bias to the view depth of the mesh which can be used to force a specific render order
/// for meshes with equal depth, to avoid z-fighting.
/// The bias is in depth-texture units so large values may be needed to overcome small depth differences.
Expand Down Expand Up @@ -1055,15 +1044,25 @@ fn prepare_material<M: Material>(
OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred,
OpaqueRendererMethod::Auto => default_opaque_render_method,
};

let mut mesh_pipeline_key_bits = MeshPipelineKey::empty();
mesh_pipeline_key_bits.set(
MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
material.reads_view_transmission_texture(),
);
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode()));

Ok(PreparedMaterial {
bindings: prepared.bindings,
bind_group: prepared.bind_group,
key: prepared.data,
properties: MaterialProperties {
alpha_mode: material.alpha_mode(),
depth_bias: material.depth_bias(),
reads_view_transmission_texture: material.reads_view_transmission_texture(),
reads_view_transmission_texture: mesh_pipeline_key_bits
.contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE),
render_method: method,
mesh_pipeline_key_bits,
},
})
}
7 changes: 2 additions & 5 deletions crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,11 +783,8 @@ pub fn queue_prepass_material_meshes<M: Material>(
continue;
};

let mut mesh_key =
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) | view_key;
if mesh.morph_targets.is_some() {
mesh_key |= MeshPipelineKey::MORPH_TARGETS;
}
let mut mesh_key = view_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits());

let alpha_mode = material.properties.alpha_mode;
match alpha_mode {
AlphaMode::Opaque => {}
Expand Down
13 changes: 5 additions & 8 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,10 @@ pub fn queue_shadows<M: Material>(
};
// NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued

let mut light_key = MeshPipelineKey::DEPTH_PREPASS;
light_key.set(MeshPipelineKey::DEPTH_CLAMP_ORTHO, is_directional_light);

for entity in visible_entities.iter().copied() {
let Some(mesh_instance) = render_mesh_instances.get(&entity) else {
continue;
Expand All @@ -1661,14 +1665,7 @@ pub fn queue_shadows<M: Material>(
};

let mut mesh_key =
MeshPipelineKey::from_primitive_topology(mesh.primitive_topology)
| MeshPipelineKey::DEPTH_PREPASS;
if mesh.morph_targets.is_some() {
mesh_key |= MeshPipelineKey::MORPH_TARGETS;
}
if is_directional_light {
mesh_key |= MeshPipelineKey::DEPTH_CLAMP_ORTHO;
}
light_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits());

// Even though we don't use the lightmap in the shadow map, the
// `SetMeshBindGroup` render command will bind the data for it. So
Expand Down
38 changes: 17 additions & 21 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,17 +491,16 @@ bitflags::bitflags! {
const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9;
const DEPTH_CLAMP_ORTHO = 1 << 10;
const TEMPORAL_JITTER = 1 << 11;
const MORPH_TARGETS = 1 << 12;
const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 13;
const LIGHTMAPPED = 1 << 14;
const IRRADIANCE_VOLUME = 1 << 15;
const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 12;
const LIGHTMAPPED = 1 << 13;
const IRRADIANCE_VOLUME = 1 << 14;
const LAST_FLAG = Self::IRRADIANCE_VOLUME.bits();
const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state
const BLEND_OPAQUE = 0 << Self::BLEND_SHIFT_BITS; // ← Values are just sequential within the mask, and can range from 0 to 3
const BLEND_PREMULTIPLIED_ALPHA = 1 << Self::BLEND_SHIFT_BITS; //
const BLEND_MULTIPLY = 2 << Self::BLEND_SHIFT_BITS; // ← We still have room for one more value without adding more bits
const BLEND_ALPHA = 3 << Self::BLEND_SHIFT_BITS;
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
const TONEMAP_METHOD_RESERVED_BITS = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
const TONEMAP_METHOD_NONE = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
const TONEMAP_METHOD_REINHARD = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
Expand All @@ -525,36 +524,32 @@ bitflags::bitflags! {
const SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM = 1 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
const SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH = 2 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
const SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA = 3 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS;
const MORPH_TARGETS = BaseMeshPipelineKey::MORPH_TARGETS.bits();
}
}

impl MeshPipelineKey {
const MSAA_MASK_BITS: u32 = 0b111;
const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();

const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111;
const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 =
Self::MSAA_SHIFT_BITS - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones();
const MSAA_SHIFT_BITS: u32 = Self::LAST_FLAG.bits().trailing_zeros();

const BLEND_MASK_BITS: u32 = 0b11;
const BLEND_SHIFT_BITS: u32 =
Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS - Self::BLEND_MASK_BITS.count_ones();
const BLEND_SHIFT_BITS: u32 = Self::MSAA_MASK_BITS.count_ones() + Self::MSAA_SHIFT_BITS;

const TONEMAP_METHOD_MASK_BITS: u32 = 0b111;
const TONEMAP_METHOD_SHIFT_BITS: u32 =
Self::BLEND_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones();
Self::BLEND_MASK_BITS.count_ones() + Self::BLEND_SHIFT_BITS;

const SHADOW_FILTER_METHOD_MASK_BITS: u32 = 0b11;
const SHADOW_FILTER_METHOD_SHIFT_BITS: u32 =
Self::TONEMAP_METHOD_SHIFT_BITS - Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones();
Self::TONEMAP_METHOD_MASK_BITS.count_ones() + Self::TONEMAP_METHOD_SHIFT_BITS;

const VIEW_PROJECTION_MASK_BITS: u32 = 0b11;
const VIEW_PROJECTION_SHIFT_BITS: u32 =
Self::SHADOW_FILTER_METHOD_SHIFT_BITS - Self::VIEW_PROJECTION_MASK_BITS.count_ones();
Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones() + Self::SHADOW_FILTER_METHOD_SHIFT_BITS;

const SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS: u32 = 0b11;
const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u32 = Self::VIEW_PROJECTION_SHIFT_BITS
- Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS.count_ones();
const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u32 =
Self::VIEW_PROJECTION_MASK_BITS.count_ones() + Self::VIEW_PROJECTION_SHIFT_BITS;

pub fn from_msaa_samples(msaa_samples: u32) -> Self {
let msaa_bits =
Expand All @@ -576,14 +571,15 @@ impl MeshPipelineKey {

pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
let primitive_topology_bits = ((primitive_topology as u32)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
& BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS)
<< BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
Self::from_bits_retain(primitive_topology_bits)
}

pub fn primitive_topology(&self) -> PrimitiveTopology {
let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
let primitive_topology_bits = (self.bits()
>> BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
& BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS;
match primitive_topology_bits {
x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList,
x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList,
Expand Down
55 changes: 53 additions & 2 deletions crates/bevy_render/src/mesh/mesh/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod conversions;
pub mod skinning;
use bevy_transform::components::Transform;
use bitflags::bitflags;
pub use wgpu::PrimitiveTopology;

use crate::{
Expand Down Expand Up @@ -1393,6 +1394,43 @@ impl From<&Indices> for IndexFormat {
}
}

bitflags! {
/// Mesh pipeline key bits start from the highest bit and go downward. PBR
/// mesh pipeline key bits start from the lowest bit and go upward. This
/// allows the PBR bits in the downstream crate `bevy_pbr` to coexist in the
/// same field without any shifts.
pcwalton marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Debug)]
pub struct BaseMeshPipelineKey: u32 {
const MORPH_TARGETS = 1 << 31;
}
}

impl BaseMeshPipelineKey {
pub const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111;
pub const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 =
31 - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones();

pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
let primitive_topology_bits = ((primitive_topology as u32)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
Self::from_bits_retain(primitive_topology_bits)
}

pub fn primitive_topology(&self) -> PrimitiveTopology {
let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
match primitive_topology_bits {
x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList,
x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList,
x if x == PrimitiveTopology::LineStrip as u32 => PrimitiveTopology::LineStrip,
x if x == PrimitiveTopology::TriangleList as u32 => PrimitiveTopology::TriangleList,
x if x == PrimitiveTopology::TriangleStrip as u32 => PrimitiveTopology::TriangleStrip,
_ => PrimitiveTopology::default(),
}
}
}

/// The GPU-representation of a [`Mesh`].
/// Consists of a vertex data buffer and an optional index data buffer.
#[derive(Debug, Clone)]
Expand All @@ -1402,10 +1440,17 @@ pub struct GpuMesh {
pub vertex_count: u32,
pub morph_targets: Option<TextureView>,
pub buffer_info: GpuBufferInfo,
pub primitive_topology: PrimitiveTopology,
pub key_bits: BaseMeshPipelineKey,
pub layout: MeshVertexBufferLayoutRef,
}

impl GpuMesh {
#[inline]
pub fn primitive_topology(&self) -> PrimitiveTopology {
self.key_bits.primitive_topology()
}
}

/// The index/vertex buffer info of a [`GpuMesh`].
#[derive(Debug, Clone)]
pub enum GpuBufferInfo {
Expand Down Expand Up @@ -1461,11 +1506,17 @@ impl RenderAsset for Mesh {
let mesh_vertex_buffer_layout =
self.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);

let mut key_bits = BaseMeshPipelineKey::from_primitive_topology(self.primitive_topology());
key_bits.set(
BaseMeshPipelineKey::MORPH_TARGETS,
self.morph_targets.is_some(),
);

Ok(GpuMesh {
vertex_buffer,
vertex_count: self.count_vertices() as u32,
buffer_info,
primitive_topology: self.primitive_topology(),
key_bits,
layout: mesh_vertex_buffer_layout,
morph_targets: self
.morph_targets
Expand Down
Loading