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.
90 lines
3.2 KiB
Rust
90 lines
3.2 KiB
Rust
#[allow(clippy::module_inception)]
|
|
mod mesh;
|
|
pub mod morph;
|
|
pub mod primitives;
|
|
|
|
use bevy_utils::HashSet;
|
|
pub use mesh::*;
|
|
pub use primitives::*;
|
|
use std::{
|
|
hash::{Hash, Hasher},
|
|
sync::Arc,
|
|
};
|
|
|
|
use crate::{prelude::Image, render_asset::RenderAssetPlugin, RenderApp};
|
|
use bevy_app::{App, Plugin};
|
|
use bevy_asset::{AssetApp, Handle};
|
|
use bevy_ecs::{entity::Entity, system::Resource};
|
|
|
|
/// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU.
|
|
pub struct MeshPlugin;
|
|
|
|
impl Plugin for MeshPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.init_asset::<Mesh>()
|
|
.init_asset::<skinning::SkinnedMeshInverseBindposes>()
|
|
.register_asset_reflect::<Mesh>()
|
|
.register_type::<Option<Handle<Image>>>()
|
|
.register_type::<Option<Vec<String>>>()
|
|
.register_type::<Option<Indices>>()
|
|
.register_type::<Indices>()
|
|
.register_type::<skinning::SkinnedMesh>()
|
|
.register_type::<Vec<Entity>>()
|
|
// 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
|
|
.add_plugins(RenderAssetPlugin::<Mesh, Image>::default());
|
|
|
|
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
|
|
return;
|
|
};
|
|
|
|
render_app.init_resource::<MeshVertexBufferLayouts>();
|
|
}
|
|
}
|
|
|
|
/// Describes the layout of the mesh vertices in GPU memory.
|
|
///
|
|
/// At most one copy of a mesh vertex buffer layout ever exists in GPU memory at
|
|
/// once. Therefore, comparing these for equality requires only a single pointer
|
|
/// comparison, and this type's [`PartialEq`] and [`Hash`] implementations take
|
|
/// advantage of this. To that end, this type doesn't implement
|
|
/// [`bevy_derive::Deref`] or [`bevy_derive::DerefMut`] in order to reduce the
|
|
/// possibility of accidental deep comparisons, which would be needlessly
|
|
/// expensive.
|
|
#[derive(Clone, Debug)]
|
|
pub struct MeshVertexBufferLayoutRef(pub Arc<MeshVertexBufferLayout>);
|
|
|
|
/// Stores the single copy of each mesh vertex buffer layout.
|
|
#[derive(Clone, Default, Resource)]
|
|
pub struct MeshVertexBufferLayouts(HashSet<Arc<MeshVertexBufferLayout>>);
|
|
|
|
impl MeshVertexBufferLayouts {
|
|
/// Inserts a new mesh vertex buffer layout in the store and returns a
|
|
/// reference to it, reusing the existing reference if this mesh vertex
|
|
/// buffer layout was already in the store.
|
|
pub(crate) fn insert(&mut self, layout: MeshVertexBufferLayout) -> MeshVertexBufferLayoutRef {
|
|
// Because the special `PartialEq` and `Hash` implementations that
|
|
// compare by pointer are on `MeshVertexBufferLayoutRef`, not on
|
|
// `Arc<MeshVertexBufferLayout>`, this compares the mesh vertex buffer
|
|
// structurally, not by pointer.
|
|
MeshVertexBufferLayoutRef(
|
|
self.0
|
|
.get_or_insert_with(&layout, |layout| Arc::new(layout.clone()))
|
|
.clone(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl PartialEq for MeshVertexBufferLayoutRef {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
Arc::ptr_eq(&self.0, &other.0)
|
|
}
|
|
}
|
|
|
|
impl Eq for MeshVertexBufferLayoutRef {}
|
|
|
|
impl Hash for MeshVertexBufferLayoutRef {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
(&*self.0 as *const MeshVertexBufferLayout as usize).hash(state);
|
|
}
|
|
}
|