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.
This commit is contained in:
parent
fc0aa4f7b1
commit
f9cc91d5a1
@ -1,7 +1,7 @@
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_reflect::{impl_type_path, Reflect};
|
||||
use bevy_render::{
|
||||
mesh::MeshVertexBufferLayout,
|
||||
mesh::MeshVertexBufferLayoutRef,
|
||||
render_asset::RenderAssets,
|
||||
render_resource::{
|
||||
AsBindGroup, AsBindGroupError, BindGroupLayout, RenderPipelineDescriptor, Shader,
|
||||
@ -68,14 +68,14 @@ pub trait MaterialExtension: Asset + AsBindGroup + Clone + Sized {
|
||||
}
|
||||
|
||||
/// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
|
||||
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayout`] as input.
|
||||
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
|
||||
/// Specialization for the base material is applied before this function is called.
|
||||
#[allow(unused_variables)]
|
||||
#[inline]
|
||||
fn specialize(
|
||||
pipeline: &MaterialExtensionPipeline,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
key: MaterialExtensionKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
Ok(())
|
||||
@ -214,7 +214,7 @@ impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
|
||||
fn specialize(
|
||||
pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
// Call the base material's specialize function
|
||||
|
@ -159,7 +159,7 @@ fn extract_lightmaps(
|
||||
|| !render_mesh_instances
|
||||
.get(&entity)
|
||||
.and_then(|mesh_instance| meshes.get(mesh_instance.mesh_asset_id))
|
||||
.is_some_and(|mesh| mesh.layout.contains(Mesh::ATTRIBUTE_UV_1.id))
|
||||
.is_some_and(|mesh| mesh.layout.0.contains(Mesh::ATTRIBUTE_UV_1.id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use bevy_render::{
|
||||
camera::TemporalJitter,
|
||||
extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
|
||||
extract_resource::ExtractResource,
|
||||
mesh::{Mesh, MeshVertexBufferLayout},
|
||||
mesh::{Mesh, MeshVertexBufferLayoutRef},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::*,
|
||||
render_resource::*,
|
||||
@ -171,13 +171,13 @@ pub trait Material: Asset + AsBindGroup + Clone + Sized {
|
||||
}
|
||||
|
||||
/// Customizes the default [`RenderPipelineDescriptor`] for a specific entity using the entity's
|
||||
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayout`] as input.
|
||||
/// [`MaterialPipelineKey`] and [`MeshVertexBufferLayoutRef`] as input.
|
||||
#[allow(unused_variables)]
|
||||
#[inline]
|
||||
fn specialize(
|
||||
pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
Ok(())
|
||||
@ -326,7 +326,7 @@ where
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
|
||||
if let Some(vertex_shader) = &self.vertex_shader {
|
||||
@ -585,6 +585,7 @@ pub fn queue_material_meshes<M: Material>(
|
||||
camera_3d.screen_space_specular_transmission_quality,
|
||||
);
|
||||
}
|
||||
|
||||
let rangefinder = view.rangefinder3d();
|
||||
for visible_entity in &visible_entities.entities {
|
||||
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
||||
|
@ -2,7 +2,9 @@ use bevy_asset::Asset;
|
||||
use bevy_color::Alpha;
|
||||
use bevy_math::{Affine2, Mat3, Vec4};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_render::{mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_resource::*};
|
||||
use bevy_render::{
|
||||
mesh::MeshVertexBufferLayoutRef, render_asset::RenderAssets, render_resource::*,
|
||||
};
|
||||
|
||||
use crate::deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID;
|
||||
use crate::*;
|
||||
@ -813,7 +815,7 @@ impl Material for StandardMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
_layout: &MeshVertexBufferLayout,
|
||||
_layout: &MeshVertexBufferLayoutRef,
|
||||
key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
if let Some(fragment) = descriptor.fragment.as_mut() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod prepass_bindings;
|
||||
|
||||
use bevy_render::mesh::MeshVertexBufferLayoutRef;
|
||||
use bevy_render::render_resource::binding_types::uniform_buffer;
|
||||
pub use prepass_bindings::*;
|
||||
|
||||
@ -17,7 +18,6 @@ use bevy_math::{Affine3A, Mat4};
|
||||
use bevy_render::{
|
||||
batching::batch_and_prepare_render_phase,
|
||||
globals::{GlobalsBuffer, GlobalsUniform},
|
||||
mesh::MeshVertexBufferLayout,
|
||||
prelude::{Camera, Mesh},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::*,
|
||||
@ -302,7 +302,7 @@ where
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut bind_group_layouts = vec![if key
|
||||
.mesh_key
|
||||
@ -347,7 +347,7 @@ where
|
||||
shader_defs.push("BLEND_ALPHA".into());
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
shader_defs.push("VERTEX_POSITIONS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||
}
|
||||
@ -363,12 +363,12 @@ where
|
||||
shader_defs.push("PREPASS_FRAGMENT".into());
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
shader_defs.push("VERTEX_UVS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_UV_1) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
|
||||
shader_defs.push("VERTEX_UVS_B".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2));
|
||||
}
|
||||
@ -383,7 +383,7 @@ where
|
||||
{
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
|
||||
shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into());
|
||||
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
shader_defs.push("VERTEX_TANGENTS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
|
||||
}
|
||||
@ -400,7 +400,7 @@ where
|
||||
shader_defs.push("DEFERRED_PREPASS".into());
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
shader_defs.push("VERTEX_COLORS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
|
||||
}
|
||||
@ -430,7 +430,7 @@ where
|
||||
);
|
||||
bind_group_layouts.insert(1, bind_group);
|
||||
|
||||
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
|
||||
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
|
||||
|
||||
// Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
|
||||
let mut targets = vec![
|
||||
|
@ -26,7 +26,7 @@ use bevy_render::{
|
||||
Extract,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::{tracing::error, Entry, HashMap, Hashed, Parallel};
|
||||
use bevy_utils::{tracing::error, Entry, HashMap, Parallel};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use bevy_utils::warn_once;
|
||||
@ -595,12 +595,13 @@ impl MeshPipelineKey {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_skinned(layout: &Hashed<InnerMeshVertexBufferLayout>) -> bool {
|
||||
layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
|
||||
fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
|
||||
layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
|
||||
&& layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
|
||||
}
|
||||
pub fn setup_morph_and_skinning_defs(
|
||||
mesh_layouts: &MeshLayouts,
|
||||
layout: &Hashed<InnerMeshVertexBufferLayout>,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
offset: u32,
|
||||
key: &MeshPipelineKey,
|
||||
shader_defs: &mut Vec<ShaderDefVal>,
|
||||
@ -638,7 +639,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut shader_defs = Vec::new();
|
||||
let mut vertex_attributes = Vec::new();
|
||||
@ -648,32 +649,32 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
||||
|
||||
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into());
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
shader_defs.push("VERTEX_POSITIONS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_NORMAL) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
|
||||
shader_defs.push("VERTEX_NORMALS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
shader_defs.push("VERTEX_UVS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_UV_1) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_UV_1) {
|
||||
shader_defs.push("VERTEX_UVS_B".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
shader_defs.push("VERTEX_TANGENTS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
shader_defs.push("VERTEX_COLORS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5));
|
||||
}
|
||||
@ -701,7 +702,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
|
||||
shader_defs.push("SCREEN_SPACE_AMBIENT_OCCLUSION".into());
|
||||
}
|
||||
|
||||
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
|
||||
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
|
||||
|
||||
let (label, blend, depth_write_enabled);
|
||||
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);
|
||||
|
@ -5,7 +5,8 @@ use bevy_color::{Color, LinearRgba};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
|
||||
use bevy_render::{
|
||||
extract_resource::ExtractResource, mesh::MeshVertexBufferLayout, prelude::*, render_resource::*,
|
||||
extract_resource::ExtractResource, mesh::MeshVertexBufferLayoutRef, prelude::*,
|
||||
render_resource::*,
|
||||
};
|
||||
|
||||
pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(192598014480025766);
|
||||
@ -208,7 +209,7 @@ impl Material for WireframeMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
_layout: &MeshVertexBufferLayout,
|
||||
_layout: &MeshVertexBufferLayoutRef,
|
||||
_key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
descriptor.primitive.polygon_mode = PolygonMode::Line;
|
||||
|
@ -13,11 +13,14 @@ use crate::{
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_core::cast_slice;
|
||||
use bevy_derive::EnumVariantMeta;
|
||||
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
|
||||
use bevy_ecs::system::{
|
||||
lifetimeless::{SRes, SResMut},
|
||||
SystemParamItem,
|
||||
};
|
||||
use bevy_log::warn;
|
||||
use bevy_math::*;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_utils::{tracing::error, Hashed};
|
||||
use bevy_utils::tracing::error;
|
||||
use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator};
|
||||
use thiserror::Error;
|
||||
use wgpu::{
|
||||
@ -25,6 +28,8 @@ use wgpu::{
|
||||
VertexStepMode,
|
||||
};
|
||||
|
||||
use super::{MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};
|
||||
|
||||
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
|
||||
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
|
||||
|
||||
@ -377,7 +382,10 @@ impl Mesh {
|
||||
/// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in [`SpecializedMeshPipeline`].
|
||||
///
|
||||
/// [`SpecializedMeshPipeline`]: crate::render_resource::SpecializedMeshPipeline
|
||||
pub fn get_mesh_vertex_buffer_layout(&self) -> MeshVertexBufferLayout {
|
||||
pub fn get_mesh_vertex_buffer_layout(
|
||||
&self,
|
||||
mesh_vertex_buffer_layouts: &mut MeshVertexBufferLayouts,
|
||||
) -> MeshVertexBufferLayoutRef {
|
||||
let mut attributes = Vec::with_capacity(self.attributes.len());
|
||||
let mut attribute_ids = Vec::with_capacity(self.attributes.len());
|
||||
let mut accumulated_offset = 0;
|
||||
@ -391,14 +399,15 @@ impl Mesh {
|
||||
accumulated_offset += data.attribute.format.get_size();
|
||||
}
|
||||
|
||||
MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout {
|
||||
let layout = MeshVertexBufferLayout {
|
||||
layout: VertexBufferLayout {
|
||||
array_stride: accumulated_offset,
|
||||
step_mode: VertexStepMode::Vertex,
|
||||
attributes,
|
||||
},
|
||||
attribute_ids,
|
||||
})
|
||||
};
|
||||
mesh_vertex_buffer_layouts.insert(layout)
|
||||
}
|
||||
|
||||
/// Counts all vertices of the mesh.
|
||||
@ -967,15 +976,13 @@ impl From<MeshVertexAttribute> for MeshVertexAttributeId {
|
||||
}
|
||||
}
|
||||
|
||||
pub type MeshVertexBufferLayout = Hashed<InnerMeshVertexBufferLayout>;
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct InnerMeshVertexBufferLayout {
|
||||
pub struct MeshVertexBufferLayout {
|
||||
attribute_ids: Vec<MeshVertexAttributeId>,
|
||||
layout: VertexBufferLayout,
|
||||
}
|
||||
|
||||
impl InnerMeshVertexBufferLayout {
|
||||
impl MeshVertexBufferLayout {
|
||||
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
|
||||
Self {
|
||||
attribute_ids,
|
||||
@ -1350,7 +1357,7 @@ pub struct GpuMesh {
|
||||
pub morph_targets: Option<TextureView>,
|
||||
pub buffer_info: GpuBufferInfo,
|
||||
pub primitive_topology: PrimitiveTopology,
|
||||
pub layout: MeshVertexBufferLayout,
|
||||
pub layout: MeshVertexBufferLayoutRef,
|
||||
}
|
||||
|
||||
/// The index/vertex buffer info of a [`GpuMesh`].
|
||||
@ -1367,7 +1374,11 @@ pub enum GpuBufferInfo {
|
||||
|
||||
impl RenderAsset for Mesh {
|
||||
type PreparedAsset = GpuMesh;
|
||||
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>);
|
||||
type Param = (
|
||||
SRes<RenderDevice>,
|
||||
SRes<RenderAssets<Image>>,
|
||||
SResMut<MeshVertexBufferLayouts>,
|
||||
);
|
||||
|
||||
fn asset_usage(&self) -> RenderAssetUsages {
|
||||
self.asset_usage
|
||||
@ -1376,7 +1387,9 @@ impl RenderAsset for Mesh {
|
||||
/// Converts the extracted mesh a into [`GpuMesh`].
|
||||
fn prepare_asset(
|
||||
self,
|
||||
(render_device, images): &mut SystemParamItem<Self::Param>,
|
||||
(render_device, images, ref mut mesh_vertex_buffer_layouts): &mut SystemParamItem<
|
||||
Self::Param,
|
||||
>,
|
||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
|
||||
let vertex_buffer_data = self.get_vertex_buffer_data();
|
||||
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
|
||||
@ -1399,7 +1412,8 @@ impl RenderAsset for Mesh {
|
||||
GpuBufferInfo::NonIndexed
|
||||
};
|
||||
|
||||
let mesh_vertex_buffer_layout = self.get_mesh_vertex_buffer_layout();
|
||||
let mesh_vertex_buffer_layout =
|
||||
self.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
|
||||
|
||||
Ok(GpuMesh {
|
||||
vertex_buffer,
|
||||
|
@ -3,13 +3,18 @@ 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};
|
||||
use crate::{prelude::Image, render_asset::RenderAssetPlugin, RenderApp};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_asset::{AssetApp, Handle};
|
||||
use bevy_ecs::entity::Entity;
|
||||
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;
|
||||
@ -27,5 +32,58 @@ impl Plugin for MeshPlugin {
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
use crate::mesh::MeshVertexBufferLayoutRef;
|
||||
use crate::render_resource::CachedComputePipelineId;
|
||||
use crate::{
|
||||
mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout, MissingVertexAttributeError},
|
||||
mesh::MissingVertexAttributeError,
|
||||
render_resource::{
|
||||
CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor,
|
||||
VertexBufferLayout,
|
||||
},
|
||||
};
|
||||
use bevy_ecs::system::Resource;
|
||||
use bevy_utils::{
|
||||
default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap,
|
||||
PreHashMapExt,
|
||||
};
|
||||
use bevy_utils::{default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap};
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
use thiserror::Error;
|
||||
|
||||
@ -79,14 +77,13 @@ pub trait SpecializedMeshPipeline {
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>;
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
|
||||
mesh_layout_cache:
|
||||
PreHashMap<InnerMeshVertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>,
|
||||
mesh_layout_cache: HashMap<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>,
|
||||
vertex_layout_cache: HashMap<VertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>,
|
||||
}
|
||||
|
||||
@ -106,12 +103,9 @@ impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
|
||||
cache: &PipelineCache,
|
||||
specialize_pipeline: &S,
|
||||
key: S::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
|
||||
let map = self
|
||||
.mesh_layout_cache
|
||||
.get_or_insert_with(layout, Default::default);
|
||||
match map.entry(key.clone()) {
|
||||
match self.mesh_layout_cache.entry((layout.clone(), key.clone())) {
|
||||
Entry::Occupied(entry) => Ok(*entry.into_mut()),
|
||||
Entry::Vacant(entry) => {
|
||||
let descriptor = specialize_pipeline
|
||||
|
@ -12,7 +12,7 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_log::error;
|
||||
use bevy_render::{
|
||||
mesh::{Mesh, MeshVertexBufferLayout},
|
||||
mesh::{Mesh, MeshVertexBufferLayoutRef},
|
||||
prelude::Image,
|
||||
render_asset::{prepare_assets, RenderAssets},
|
||||
render_phase::{
|
||||
@ -125,7 +125,7 @@ pub trait Material2d: AsBindGroup + Asset + Clone + Sized {
|
||||
#[inline]
|
||||
fn specialize(
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
key: Material2dKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
Ok(())
|
||||
@ -271,7 +271,7 @@ where
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut descriptor = self.mesh2d_pipeline.specialize(key.mesh_key, layout)?;
|
||||
if let Some(vertex_shader) = &self.vertex_shader {
|
||||
|
@ -11,13 +11,14 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_math::{Affine3, Vec4};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::mesh::MeshVertexBufferLayoutRef;
|
||||
use bevy_render::{
|
||||
batching::{
|
||||
batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData,
|
||||
NoAutomaticBatching,
|
||||
},
|
||||
globals::{GlobalsBuffer, GlobalsUniform},
|
||||
mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout},
|
||||
mesh::{GpuBufferInfo, Mesh},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
|
||||
render_resource::{binding_types::uniform_buffer, *},
|
||||
@ -436,32 +437,32 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut shader_defs = Vec::new();
|
||||
let mut vertex_attributes = Vec::new();
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
|
||||
shader_defs.push("VERTEX_POSITIONS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_NORMAL) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
|
||||
shader_defs.push("VERTEX_NORMALS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
|
||||
shader_defs.push("VERTEX_UVS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
|
||||
shader_defs.push("VERTEX_TANGENTS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
|
||||
}
|
||||
|
||||
if layout.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
|
||||
shader_defs.push("VERTEX_COLORS".into());
|
||||
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
|
||||
}
|
||||
@ -504,7 +505,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?;
|
||||
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
|
||||
|
||||
let format = match key.contains(Mesh2dPipelineKey::HDR) {
|
||||
true => ViewTarget::TEXTURE_FORMAT_HDR,
|
||||
|
@ -5,7 +5,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
render::{
|
||||
mesh::{MeshVertexAttribute, MeshVertexBufferLayout},
|
||||
mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef},
|
||||
render_resource::*,
|
||||
},
|
||||
sprite::{Material2d, Material2dKey, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle},
|
||||
@ -70,10 +70,10 @@ impl Material2d for CustomMaterial {
|
||||
|
||||
fn specialize(
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
_key: Material2dKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
let vertex_layout = layout.get_layout(&[
|
||||
let vertex_layout = layout.0.get_layout(&[
|
||||
Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
||||
Mesh::ATTRIBUTE_COLOR.at_shader_location(1),
|
||||
ATTRIBUTE_BARYCENTRIC.at_shader_location(2),
|
||||
|
@ -5,7 +5,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
render::{
|
||||
mesh::{MeshVertexBufferLayout, PrimitiveTopology},
|
||||
mesh::{MeshVertexBufferLayoutRef, PrimitiveTopology},
|
||||
render_asset::RenderAssetUsages,
|
||||
render_resource::{
|
||||
AsBindGroup, PolygonMode, RenderPipelineDescriptor, ShaderRef,
|
||||
@ -78,7 +78,7 @@ impl Material for LineMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
_layout: &MeshVertexBufferLayout,
|
||||
_layout: &MeshVertexBufferLayoutRef,
|
||||
_key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
// This is the important part to tell bevy to render this material as a line between vertices
|
||||
|
@ -5,7 +5,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
render::{
|
||||
mesh::{MeshVertexAttribute, MeshVertexBufferLayout},
|
||||
mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef},
|
||||
render_resource::{
|
||||
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
|
||||
VertexFormat,
|
||||
@ -74,10 +74,10 @@ impl Material for CustomMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
_key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
let vertex_layout = layout.get_layout(&[
|
||||
let vertex_layout = layout.0.get_layout(&[
|
||||
Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
|
||||
ATTRIBUTE_BLEND_COLOR.at_shader_location(1),
|
||||
])?;
|
||||
|
@ -5,7 +5,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
render::{
|
||||
mesh::MeshVertexBufferLayout,
|
||||
mesh::MeshVertexBufferLayoutRef,
|
||||
render_resource::{
|
||||
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
|
||||
},
|
||||
@ -62,7 +62,7 @@ impl Material for CustomMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
_layout: &MeshVertexBufferLayout,
|
||||
_layout: &MeshVertexBufferLayoutRef,
|
||||
key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
if key.bind_group_data.is_red {
|
||||
|
@ -12,7 +12,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
render::{
|
||||
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||
mesh::{GpuBufferInfo, MeshVertexBufferLayout},
|
||||
mesh::{GpuBufferInfo, MeshVertexBufferLayoutRef},
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{
|
||||
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
|
||||
@ -197,7 +197,7 @@ impl SpecializedMeshPipeline for CustomPipeline {
|
||||
fn specialize(
|
||||
&self,
|
||||
key: Self::Key,
|
||||
layout: &MeshVertexBufferLayout,
|
||||
layout: &MeshVertexBufferLayoutRef,
|
||||
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
|
||||
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;
|
||||
|
||||
|
@ -5,7 +5,7 @@ use bevy::{
|
||||
prelude::*,
|
||||
reflect::TypePath,
|
||||
render::{
|
||||
mesh::MeshVertexBufferLayout,
|
||||
mesh::MeshVertexBufferLayoutRef,
|
||||
render_resource::{
|
||||
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
|
||||
},
|
||||
@ -78,7 +78,7 @@ impl Material for CustomMaterial {
|
||||
fn specialize(
|
||||
_pipeline: &MaterialPipeline<Self>,
|
||||
descriptor: &mut RenderPipelineDescriptor,
|
||||
_layout: &MeshVertexBufferLayout,
|
||||
_layout: &MeshVertexBufferLayoutRef,
|
||||
_key: MaterialPipelineKey<Self>,
|
||||
) -> Result<(), SpecializedMeshPipelineError> {
|
||||
descriptor.vertex.entry_point = "main".into();
|
||||
|
@ -18,8 +18,9 @@ use bevy::{
|
||||
render::{
|
||||
render_asset::RenderAssetUsages,
|
||||
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||
view::NoFrustumCulling,
|
||||
},
|
||||
window::{PresentMode, WindowPlugin, WindowResolution},
|
||||
window::{PresentMode, WindowResolution},
|
||||
winit::{UpdateMode, WinitSettings},
|
||||
};
|
||||
use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};
|
||||
@ -42,6 +43,10 @@ struct Args {
|
||||
/// the number of different textures from which to randomly select the material base color. 0 means no textures.
|
||||
#[argh(option, default = "0")]
|
||||
material_texture_count: usize,
|
||||
|
||||
/// whether to disable frustum culling, for stress testing purposes
|
||||
#[argh(switch)]
|
||||
no_frustum_culling: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
@ -131,12 +136,15 @@ fn setup(
|
||||
let spherical_polar_theta_phi =
|
||||
fibonacci_spiral_on_sphere(golden_ratio, i, N_POINTS);
|
||||
let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
|
||||
commands.spawn(PbrBundle {
|
||||
let mut cube = commands.spawn(PbrBundle {
|
||||
mesh: mesh.clone(),
|
||||
material: materials.choose(&mut material_rng).unwrap().clone(),
|
||||
transform: Transform::from_translation((radius * unit_sphere_p).as_vec3()),
|
||||
..default()
|
||||
});
|
||||
if args.no_frustum_culling {
|
||||
cube.insert(NoFrustumCulling);
|
||||
}
|
||||
}
|
||||
|
||||
// camera
|
||||
|
Loading…
Reference in New Issue
Block a user