Skip to content

Commit f9cc91d

Browse files
authored
Intern mesh vertex buffer layouts so that we don't have to compare them over and over. (#12216)
Although we cached hashes of `MeshVertexBufferLayout`, we were paying the cost of `PartialEq` on `InnerMeshVertexBufferLayout` for every entity, every frame. This patch changes that logic to place `MeshVertexBufferLayout`s in `Arc`s so that they can be compared and hashed by pointer. This results in a 28% speedup in the `queue_material_meshes` phase of `many_cubes`, with frustum culling disabled. Additionally, this patch contains two minor changes: 1. This commit flattens the specialized mesh pipeline cache to one level of hash tables instead of two. This saves a hash lookup. 2. The example `many_cubes` has been given a `--no-frustum-culling` flag, to aid in benchmarking. See the Tracy profile: <img width="1064" alt="Screenshot 2024-02-29 144406" src="https://github.com/bevyengine/bevy/assets/157897/18632f1d-1fdd-4ac7-90ed-2d10306b2a1e"> ## Migration guide * Duplicate `MeshVertexBufferLayout`s are now combined into a single object, `MeshVertexBufferLayoutRef`, which contains an atomically-reference-counted pointer to the layout. Code that was using `MeshVertexBufferLayout` may need to be updated to use `MeshVertexBufferLayoutRef` instead.
1 parent fc0aa4f commit f9cc91d

File tree

19 files changed

+168
-88
lines changed

19 files changed

+168
-88
lines changed

crates/bevy_pbr/src/extended_material.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use bevy_asset::{Asset, Handle};
22
use bevy_reflect::{impl_type_path, Reflect};
33
use bevy_render::{
4-
mesh::MeshVertexBufferLayout,
4+
mesh::MeshVertexBufferLayoutRef,
55
render_asset::RenderAssets,
66
render_resource::{
77
AsBindGroup, AsBindGroupError, BindGroupLayout, RenderPipelineDescriptor, Shader,
@@ -68,14 +68,14 @@ pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
6868
}
6969

7070
/// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
71-
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayout`] as input.
71+
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
7272
/// Specialization for the base material is applied before this function is called.
7373
#[allow(unused_variables)]
7474
#[inline]
7575
fn specialize(
7676
pipeline: &MaterialExtensionPipeline,
7777
descriptor: &mut RenderPipelineDescriptor,
78-
layout: &MeshVertexBufferLayout,
78+
layout: &MeshVertexBufferLayoutRef,
7979
key: MaterialExtensionKey<Self>,
8080
) -> Result<(), SpecializedMeshPipelineError> {
8181
Ok(())
@@ -214,7 +214,7 @@ impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
214214
fn specialize(
215215
pipeline: &MaterialPipeline<Self>,
216216
descriptor: &mut RenderPipelineDescriptor,
217-
layout: &MeshVertexBufferLayout,
217+
layout: &MeshVertexBufferLayoutRef,
218218
key: MaterialPipelineKey<Self>,
219219
) -> Result<(), SpecializedMeshPipelineError> {
220220
// Call the base material's specialize function

crates/bevy_pbr/src/lightmap/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ fn extract_lightmaps(
159159
|| !render_mesh_instances
160160
.get(&entity)
161161
.and_then(|mesh_instance| meshes.get(mesh_instance.mesh_asset_id))
162-
.is_some_and(|mesh| mesh.layout.contains(Mesh::ATTRIBUTE_UV_1.id))
162+
.is_some_and(|mesh| mesh.layout.0.contains(Mesh::ATTRIBUTE_UV_1.id))
163163
{
164164
continue;
165165
}

crates/bevy_pbr/src/material.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use bevy_render::{
1818
camera::TemporalJitter,
1919
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
2020
extract_resource::ExtractResource,
21-
mesh::{Mesh, MeshVertexBufferLayout},
21+
mesh::{Mesh, MeshVertexBufferLayoutRef},
2222
render_asset::RenderAssets,
2323
render_phase::*,
2424
render_resource::*,
@@ -171,13 +171,13 @@ pub trait Material: Asset + AsBindGroup + Clone + Sized {
171171
}
172172

173173
/// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
174-
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayout`] as input.
174+
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
175175
#[allow(unused_variables)]
176176
#[inline]
177177
fn specialize(
178178
pipeline: &MaterialPipeline<Self>,
179179
descriptor: &mut RenderPipelineDescriptor,
180-
layout: &MeshVertexBufferLayout,
180+
layout: &MeshVertexBufferLayoutRef,
181181
key: MaterialPipelineKey<Self>,
182182
) -> Result<(), SpecializedMeshPipelineError> {
183183
Ok(())
@@ -326,7 +326,7 @@ where
326326
fn specialize(
327327
&self,
328328
key: Self::Key,
329-
layout: &MeshVertexBufferLayout,
329+
layout: &MeshVertexBufferLayoutRef,
330330
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
331331
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
332332
if let Some(vertex_shader) = &self.vertex_shader {
@@ -585,6 +585,7 @@ pub fn queue_material_meshes<M: Material>(
585585
camera_3d.screen_space_specular_transmission_quality,
586586
);
587587
}
588+
588589
let rangefinder = view.rangefinder3d();
589590
for visible_entity in &visible_entities.entities {
590591
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {

crates/bevy_pbr/src/pbr_material.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use bevy_asset::Asset;
22
use bevy_color::Alpha;
33
use bevy_math::{Affine2, Mat3, Vec4};
44
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5-
use bevy_render::{mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*};
5+
use bevy_render::{
6+
mesh::MeshVertexBufferLayoutRef, render_asset::RenderAssets, render_resource::*,
7+
};
68

79
use crate::deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID;
810
use crate::*;
@@ -813,7 +815,7 @@ impl Material for StandardMaterial {
813815
fn specialize(
814816
_pipeline: &MaterialPipeline<Self>,
815817
descriptor: &mut RenderPipelineDescriptor,
816-
_layout: &MeshVertexBufferLayout,
818+
_layout: &MeshVertexBufferLayoutRef,
817819
key: MaterialPipelineKey<Self>,
818820
) -> Result<(), SpecializedMeshPipelineError> {
819821
if let Some(fragment) = descriptor.fragment.as_mut() {

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod prepass_bindings;
22

3+
use bevy_render::mesh::MeshVertexBufferLayoutRef;
34
use bevy_render::render_resource::binding_types::uniform_buffer;
45
pub use prepass_bindings::*;
56

@@ -17,7 +18,6 @@ use bevy_math::{Affine3A, Mat4};
1718
use bevy_render::{
1819
batching::batch_and_prepare_render_phase,
1920
globals::{GlobalsBuffer, GlobalsUniform},
20-
mesh::MeshVertexBufferLayout,
2121
prelude::{Camera, Mesh},
2222
render_asset::RenderAssets,
2323
render_phase::*,
@@ -302,7 +302,7 @@ where
302302
fn specialize(
303303
&self,
304304
key: Self::Key,
305-
layout: &MeshVertexBufferLayout,
305+
layout: &MeshVertexBufferLayoutRef,
306306
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
307307
let mut bind_group_layouts = vec![if key
308308
.mesh_key
@@ -347,7 +347,7 @@ where
347347
shader_defs.push("BLEND_ALPHA".into());
348348
}
349349

350-
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
350+
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
351351
shader_defs.push("VERTEX_POSITIONS".into());
352352
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
353353
}
@@ -363,12 +363,12 @@ where
363363
shader_defs.push("PREPASS_FRAGMENT".into());
364364
}
365365

366-
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
366+
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
367367
shader_defs.push("VERTEX_UVS".into());
368368
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
369369
}
370370

371-
if layout.contains(Mesh::ATTRIBUTE_UV_1) {
371+
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
372372
shader_defs.push("VERTEX_UVS_B".into());
373373
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
374374
}
@@ -383,7 +383,7 @@ where
383383
{
384384
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
385385
shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
386-
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
386+
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
387387
shader_defs.push("VERTEX_TANGENTS".into());
388388
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
389389
}
@@ -400,7 +400,7 @@ where
400400
shader_defs.push("DEFERRED_PREPASS".into());
401401
}
402402

403-
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
403+
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
404404
shader_defs.push("VERTEX_COLORS".into());
405405
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
406406
}
@@ -430,7 +430,7 @@ where
430430
);
431431
bind_group_layouts.insert(1, bind_group);
432432

433-
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
433+
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
434434

435435
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
436436
let mut targets = vec![

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use bevy_render::{
2626
Extract,
2727
};
2828
use bevy_transform::components::GlobalTransform;
29-
use bevy_utils::{tracing::error, Entry, HashMap, Hashed, Parallel};
29+
use bevy_utils::{tracing::error, Entry, HashMap, Parallel};
3030

3131
#[cfg(debug_assertions)]
3232
use bevy_utils::warn_once;
@@ -595,12 +595,13 @@ impl MeshPipelineKey {
595595
}
596596
}
597597

598-
fn is_skinned(layout: &Hashed<InnerMeshVertexBufferLayout>) -> bool {
599-
layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
598+
fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
599+
layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
600+
&& layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
600601
}
601602
pub fn setup_morph_and_skinning_defs(
602603
mesh_layouts: &MeshLayouts,
603-
layout: &Hashed<InnerMeshVertexBufferLayout>,
604+
layout: &MeshVertexBufferLayoutRef,
604605
offset: u32,
605606
key: &MeshPipelineKey,
606607
shader_defs: &mut Vec<ShaderDefVal>,
@@ -638,7 +639,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
638639
fn specialize(
639640
&self,
640641
key: Self::Key,
641-
layout: &MeshVertexBufferLayout,
642+
layout: &MeshVertexBufferLayoutRef,
642643
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
643644
let mut shader_defs = Vec::new();
644645
let mut vertex_attributes = Vec::new();
@@ -648,32 +649,32 @@ impl SpecializedMeshPipeline for MeshPipeline {
648649

649650
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
650651

651-
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
652+
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
652653
shader_defs.push("VERTEX_POSITIONS".into());
653654
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
654655
}
655656

656-
if layout.contains(Mesh::ATTRIBUTE_NORMAL) {
657+
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
657658
shader_defs.push("VERTEX_NORMALS".into());
658659
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
659660
}
660661

661-
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
662+
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
662663
shader_defs.push("VERTEX_UVS".into());
663664
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
664665
}
665666

666-
if layout.contains(Mesh::ATTRIBUTE_UV_1) {
667+
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
667668
shader_defs.push("VERTEX_UVS_B".into());
668669
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3));
669670
}
670671

671-
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
672+
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
672673
shader_defs.push("VERTEX_TANGENTS".into());
673674
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
674675
}
675676

676-
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
677+
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
677678
shader_defs.push("VERTEX_COLORS".into());
678679
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5));
679680
}
@@ -701,7 +702,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
701702
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
702703
}
703704

704-
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
705+
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
705706

706707
let (label, blend, depth_write_enabled);
707708
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);

crates/bevy_pbr/src/wireframe.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use bevy_color::{Color, LinearRgba};
55
use bevy_ecs::prelude::*;
66
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
77
use bevy_render::{
8-
extract_resource::ExtractResource, mesh::MeshVertexBufferLayout, prelude::*, render_resource::*,
8+
extract_resource::ExtractResource, mesh::MeshVertexBufferLayoutRef, prelude::*,
9+
render_resource::*,
910
};
1011

1112
pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(192598014480025766);
@@ -208,7 +209,7 @@ impl Material for WireframeMaterial {
208209
fn specialize(
209210
_pipeline: &MaterialPipeline<Self>,
210211
descriptor: &mut RenderPipelineDescriptor,
211-
_layout: &MeshVertexBufferLayout,
212+
_layout: &MeshVertexBufferLayoutRef,
212213
_key: MaterialPipelineKey<Self>,
213214
) -> Result<(), SpecializedMeshPipelineError> {
214215
descriptor.primitive.polygon_mode = PolygonMode::Line;

crates/bevy_render/src/mesh/mesh/mod.rs

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,23 @@ use crate::{
1313
use bevy_asset::{Asset, Handle};
1414
use bevy_core::cast_slice;
1515
use bevy_derive::EnumVariantMeta;
16-
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
16+
use bevy_ecs::system::{
17+
lifetimeless::{SRes, SResMut},
18+
SystemParamItem,
19+
};
1720
use bevy_log::warn;
1821
use bevy_math::*;
1922
use bevy_reflect::Reflect;
20-
use bevy_utils::{tracing::error, Hashed};
23+
use bevy_utils::tracing::error;
2124
use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator};
2225
use thiserror::Error;
2326
use wgpu::{
2427
util::BufferInitDescriptor, BufferUsages, IndexFormat, VertexAttribute, VertexFormat,
2528
VertexStepMode,
2629
};
2730

31+
use super::{MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};
32+
2833
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
2934
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
3035

@@ -377,7 +382,10 @@ impl Mesh {
377382
/// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in [`SpecializedMeshPipeline`].
378383
///
379384
/// [`SpecializedMeshPipeline`]: crate::render_resource::SpecializedMeshPipeline
380-
pub fn get_mesh_vertex_buffer_layout(&self) -> MeshVertexBufferLayout {
385+
pub fn get_mesh_vertex_buffer_layout(
386+
&self,
387+
mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
388+
) -> MeshVertexBufferLayoutRef {
381389
let mut attributes = Vec::with_capacity(self.attributes.len());
382390
let mut attribute_ids = Vec::with_capacity(self.attributes.len());
383391
let mut accumulated_offset = 0;
@@ -391,14 +399,15 @@ impl Mesh {
391399
accumulated_offset += data.attribute.format.get_size();
392400
}
393401

394-
MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout {
402+
let layout = MeshVertexBufferLayout {
395403
layout: VertexBufferLayout {
396404
array_stride: accumulated_offset,
397405
step_mode: VertexStepMode::Vertex,
398406
attributes,
399407
},
400408
attribute_ids,
401-
})
409+
};
410+
mesh_vertex_buffer_layouts.insert(layout)
402411
}
403412

404413
/// Counts all vertices of the mesh.
@@ -967,15 +976,13 @@ impl From<MeshVertexAttribute> for MeshVertexAttributeId {
967976
}
968977
}
969978

970-
pub type MeshVertexBufferLayout = Hashed<InnerMeshVertexBufferLayout>;
971-
972979
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
973-
pub struct InnerMeshVertexBufferLayout {
980+
pub struct MeshVertexBufferLayout {
974981
attribute_ids: Vec<MeshVertexAttributeId>,
975982
layout: VertexBufferLayout,
976983
}
977984

978-
impl InnerMeshVertexBufferLayout {
985+
impl MeshVertexBufferLayout {
979986
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
980987
Self {
981988
attribute_ids,
@@ -1350,7 +1357,7 @@ pub struct GpuMesh {
13501357
pub morph_targets: Option<TextureView>,
13511358
pub buffer_info: GpuBufferInfo,
13521359
pub primitive_topology: PrimitiveTopology,
1353-
pub layout: MeshVertexBufferLayout,
1360+
pub layout: MeshVertexBufferLayoutRef,
13541361
}
13551362

13561363
/// The index/vertex buffer info of a [`GpuMesh`].
@@ -1367,7 +1374,11 @@ pub enum GpuBufferInfo {
13671374

13681375
impl RenderAsset for Mesh {
13691376
type PreparedAsset = GpuMesh;
1370-
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>);
1377+
type Param = (
1378+
SRes<RenderDevice>,
1379+
SRes<RenderAssets<Image>>,
1380+
SResMut<MeshVertexBufferLayouts>,
1381+
);
13711382

13721383
fn asset_usage(&self) -> RenderAssetUsages {
13731384
self.asset_usage
@@ -1376,7 +1387,9 @@ impl RenderAsset for Mesh {
13761387
/// Converts the extracted mesh a into [`GpuMesh`].
13771388
fn prepare_asset(
13781389
self,
1379-
(render_device, images): &mut SystemParamItem<Self::Param>,
1390+
(render_device, images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<
1391+
Self::Param,
1392+
>,
13801393
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
13811394
let vertex_buffer_data = self.get_vertex_buffer_data();
13821395
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
@@ -1399,7 +1412,8 @@ impl RenderAsset for Mesh {
13991412
GpuBufferInfo::NonIndexed
14001413
};
14011414

1402-
let mesh_vertex_buffer_layout = self.get_mesh_vertex_buffer_layout();
1415+
let mesh_vertex_buffer_layout =
1416+
self.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
14031417

14041418
Ok(GpuMesh {
14051419
vertex_buffer,

0 commit comments

Comments
 (0)