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:
Patrick Walton 2024-03-01 12:56:21 -08:00 committed by GitHub
parent fc0aa4f7b1
commit f9cc91d5a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 168 additions and 88 deletions

View File

@ -1,7 +1,7 @@
use bevy_asset::{Asset, Handle}; use bevy_asset::{Asset, Handle};
use bevy_reflect::{impl_type_path, Reflect}; use bevy_reflect::{impl_type_path, Reflect};
use bevy_render::{ use bevy_render::{
mesh::MeshVertexBufferLayout, mesh::MeshVertexBufferLayoutRef,
render_asset::RenderAssets, render_asset::RenderAssets,
render_resource::{ render_resource::{
AsBindGroup, AsBindGroupError, BindGroupLayout, RenderPipelineDescriptor, Shader, 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 /// 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. /// Specialization for the base material is applied before this function is called.
#[allow(unused_variables)] #[allow(unused_variables)]
#[inline] #[inline]
fn specialize( fn specialize(
pipeline: &MaterialExtensionPipeline, pipeline: &MaterialExtensionPipeline,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
key: MaterialExtensionKey<Self>, key: MaterialExtensionKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
Ok(()) Ok(())
@ -214,7 +214,7 @@ impl<B: Material, E: MaterialExtension> Material for ExtendedMaterial<B, E> {
fn specialize( fn specialize(
pipeline: &MaterialPipeline<Self>, pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>, key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
// Call the base material's specialize function // Call the base material's specialize function

View File

@ -159,7 +159,7 @@ fn extract_lightmaps(
|| !render_mesh_instances || !render_mesh_instances
.get(&entity) .get(&entity)
.and_then(|mesh_instance| meshes.get(mesh_instance.mesh_asset_id)) .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; continue;
} }

View File

@ -18,7 +18,7 @@ use bevy_render::{
camera::TemporalJitter, camera::TemporalJitter,
extract_instances::{ExtractInstancesPlugin, ExtractedInstances}, extract_instances::{ExtractInstancesPlugin, ExtractedInstances},
extract_resource::ExtractResource, extract_resource::ExtractResource,
mesh::{Mesh, MeshVertexBufferLayout}, mesh::{Mesh, MeshVertexBufferLayoutRef},
render_asset::RenderAssets, render_asset::RenderAssets,
render_phase::*, render_phase::*,
render_resource::*, 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 /// 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)] #[allow(unused_variables)]
#[inline] #[inline]
fn specialize( fn specialize(
pipeline: &MaterialPipeline<Self>, pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>, key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
Ok(()) Ok(())
@ -326,7 +326,7 @@ where
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?; let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?;
if let Some(vertex_shader) = &self.vertex_shader { 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, camera_3d.screen_space_specular_transmission_quality,
); );
} }
let rangefinder = view.rangefinder3d(); let rangefinder = view.rangefinder3d();
for visible_entity in &visible_entities.entities { for visible_entity in &visible_entities.entities {
let Some(material_asset_id) = render_material_instances.get(visible_entity) else { let Some(material_asset_id) = render_material_instances.get(visible_entity) else {

View File

@ -2,7 +2,9 @@ use bevy_asset::Asset;
use bevy_color::Alpha; use bevy_color::Alpha;
use bevy_math::{Affine2, Mat3, Vec4}; use bevy_math::{Affine2, Mat3, Vec4};
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; 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::deferred::DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID;
use crate::*; use crate::*;
@ -813,7 +815,7 @@ impl Material for StandardMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout, _layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>, key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
if let Some(fragment) = descriptor.fragment.as_mut() { if let Some(fragment) = descriptor.fragment.as_mut() {

View File

@ -1,5 +1,6 @@
mod prepass_bindings; mod prepass_bindings;
use bevy_render::mesh::MeshVertexBufferLayoutRef;
use bevy_render::render_resource::binding_types::uniform_buffer; use bevy_render::render_resource::binding_types::uniform_buffer;
pub use prepass_bindings::*; pub use prepass_bindings::*;
@ -17,7 +18,6 @@ use bevy_math::{Affine3A, Mat4};
use bevy_render::{ use bevy_render::{
batching::batch_and_prepare_render_phase, batching::batch_and_prepare_render_phase,
globals::{GlobalsBuffer, GlobalsUniform}, globals::{GlobalsBuffer, GlobalsUniform},
mesh::MeshVertexBufferLayout,
prelude::{Camera, Mesh}, prelude::{Camera, Mesh},
render_asset::RenderAssets, render_asset::RenderAssets,
render_phase::*, render_phase::*,
@ -302,7 +302,7 @@ where
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut bind_group_layouts = vec![if key let mut bind_group_layouts = vec![if key
.mesh_key .mesh_key
@ -347,7 +347,7 @@ where
shader_defs.push("BLEND_ALPHA".into()); 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()); shader_defs.push("VERTEX_POSITIONS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
} }
@ -363,12 +363,12 @@ where
shader_defs.push("PREPASS_FRAGMENT".into()); 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()); shader_defs.push("VERTEX_UVS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(1)); 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()); shader_defs.push("VERTEX_UVS_B".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(2)); 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)); vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(3));
shader_defs.push("NORMAL_PREPASS_OR_DEFERRED_PREPASS".into()); 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()); shader_defs.push("VERTEX_TANGENTS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4)); vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4));
} }
@ -400,7 +400,7 @@ where
shader_defs.push("DEFERRED_PREPASS".into()); 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()); shader_defs.push("VERTEX_COLORS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7)); vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(7));
} }
@ -430,7 +430,7 @@ where
); );
bind_group_layouts.insert(1, bind_group); 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 // Setup prepass fragment targets - normals in slot 0 (or None if not needed), motion vectors in slot 1
let mut targets = vec![ let mut targets = vec![

View File

@ -26,7 +26,7 @@ use bevy_render::{
Extract, Extract,
}; };
use bevy_transform::components::GlobalTransform; 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)] #[cfg(debug_assertions)]
use bevy_utils::warn_once; use bevy_utils::warn_once;
@ -595,12 +595,13 @@ impl MeshPipelineKey {
} }
} }
fn is_skinned(layout: &Hashed<InnerMeshVertexBufferLayout>) -> bool { fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool {
layout.contains(Mesh::ATTRIBUTE_JOINT_INDEX) && layout.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX)
&& layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT)
} }
pub fn setup_morph_and_skinning_defs( pub fn setup_morph_and_skinning_defs(
mesh_layouts: &MeshLayouts, mesh_layouts: &MeshLayouts,
layout: &Hashed<InnerMeshVertexBufferLayout>, layout: &MeshVertexBufferLayoutRef,
offset: u32, offset: u32,
key: &MeshPipelineKey, key: &MeshPipelineKey,
shader_defs: &mut Vec<ShaderDefVal>, shader_defs: &mut Vec<ShaderDefVal>,
@ -638,7 +639,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut shader_defs = Vec::new(); let mut shader_defs = Vec::new();
let mut vertex_attributes = Vec::new(); let mut vertex_attributes = Vec::new();
@ -648,32 +649,32 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("VERTEX_OUTPUT_INSTANCE_INDEX".into()); 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()); shader_defs.push("VERTEX_POSITIONS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); 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()); shader_defs.push("VERTEX_NORMALS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1)); 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()); shader_defs.push("VERTEX_UVS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2)); 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()); shader_defs.push("VERTEX_UVS_B".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_1.at_shader_location(3)); 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()); shader_defs.push("VERTEX_TANGENTS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(4)); 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()); shader_defs.push("VERTEX_COLORS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(5)); 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()); 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 (label, blend, depth_write_enabled);
let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS); let pass = key.intersection(MeshPipelineKey::BLEND_RESERVED_BITS);

View File

@ -5,7 +5,8 @@ use bevy_color::{Color, LinearRgba};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath};
use bevy_render::{ 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); pub const WIREFRAME_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(192598014480025766);
@ -208,7 +209,7 @@ impl Material for WireframeMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout, _layout: &MeshVertexBufferLayoutRef,
_key: MaterialPipelineKey<Self>, _key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
descriptor.primitive.polygon_mode = PolygonMode::Line; descriptor.primitive.polygon_mode = PolygonMode::Line;

View File

@ -13,11 +13,14 @@ use crate::{
use bevy_asset::{Asset, Handle}; use bevy_asset::{Asset, Handle};
use bevy_core::cast_slice; use bevy_core::cast_slice;
use bevy_derive::EnumVariantMeta; 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_log::warn;
use bevy_math::*; use bevy_math::*;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_utils::{tracing::error, Hashed}; use bevy_utils::tracing::error;
use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator}; use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator};
use thiserror::Error; use thiserror::Error;
use wgpu::{ use wgpu::{
@ -25,6 +28,8 @@ use wgpu::{
VertexStepMode, VertexStepMode,
}; };
use super::{MeshVertexBufferLayoutRef, MeshVertexBufferLayouts};
pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0;
pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10;
@ -377,7 +382,10 @@ impl Mesh {
/// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in [`SpecializedMeshPipeline`]. /// Get this `Mesh`'s [`MeshVertexBufferLayout`], used in [`SpecializedMeshPipeline`].
/// ///
/// [`SpecializedMeshPipeline`]: crate::render_resource::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 attributes = Vec::with_capacity(self.attributes.len());
let mut attribute_ids = Vec::with_capacity(self.attributes.len()); let mut attribute_ids = Vec::with_capacity(self.attributes.len());
let mut accumulated_offset = 0; let mut accumulated_offset = 0;
@ -391,14 +399,15 @@ impl Mesh {
accumulated_offset += data.attribute.format.get_size(); accumulated_offset += data.attribute.format.get_size();
} }
MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout { let layout = MeshVertexBufferLayout {
layout: VertexBufferLayout { layout: VertexBufferLayout {
array_stride: accumulated_offset, array_stride: accumulated_offset,
step_mode: VertexStepMode::Vertex, step_mode: VertexStepMode::Vertex,
attributes, attributes,
}, },
attribute_ids, attribute_ids,
}) };
mesh_vertex_buffer_layouts.insert(layout)
} }
/// Counts all vertices of the mesh. /// 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)] #[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct InnerMeshVertexBufferLayout { pub struct MeshVertexBufferLayout {
attribute_ids: Vec<MeshVertexAttributeId>, attribute_ids: Vec<MeshVertexAttributeId>,
layout: VertexBufferLayout, layout: VertexBufferLayout,
} }
impl InnerMeshVertexBufferLayout { impl MeshVertexBufferLayout {
pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self { pub fn new(attribute_ids: Vec<MeshVertexAttributeId>, layout: VertexBufferLayout) -> Self {
Self { Self {
attribute_ids, attribute_ids,
@ -1350,7 +1357,7 @@ pub struct GpuMesh {
pub morph_targets: Option<TextureView>, pub morph_targets: Option<TextureView>,
pub buffer_info: GpuBufferInfo, pub buffer_info: GpuBufferInfo,
pub primitive_topology: PrimitiveTopology, pub primitive_topology: PrimitiveTopology,
pub layout: MeshVertexBufferLayout, pub layout: MeshVertexBufferLayoutRef,
} }
/// The index/vertex buffer info of a [`GpuMesh`]. /// The index/vertex buffer info of a [`GpuMesh`].
@ -1367,7 +1374,11 @@ pub enum GpuBufferInfo {
impl RenderAsset for Mesh { impl RenderAsset for Mesh {
type PreparedAsset = GpuMesh; type PreparedAsset = GpuMesh;
type Param = (SRes<RenderDevice>, SRes<RenderAssets<Image>>); type Param = (
SRes<RenderDevice>,
SRes<RenderAssets<Image>>,
SResMut<MeshVertexBufferLayouts>,
);
fn asset_usage(&self) -> RenderAssetUsages { fn asset_usage(&self) -> RenderAssetUsages {
self.asset_usage self.asset_usage
@ -1376,7 +1387,9 @@ impl RenderAsset for Mesh {
/// Converts the extracted mesh a into [`GpuMesh`]. /// Converts the extracted mesh a into [`GpuMesh`].
fn prepare_asset( fn prepare_asset(
self, 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>> { ) -> Result<Self::PreparedAsset, PrepareAssetError<Self>> {
let vertex_buffer_data = self.get_vertex_buffer_data(); let vertex_buffer_data = self.get_vertex_buffer_data();
let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
@ -1399,7 +1412,8 @@ impl RenderAsset for Mesh {
GpuBufferInfo::NonIndexed 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 { Ok(GpuMesh {
vertex_buffer, vertex_buffer,

View File

@ -3,13 +3,18 @@ mod mesh;
pub mod morph; pub mod morph;
pub mod primitives; pub mod primitives;
use bevy_utils::HashSet;
pub use mesh::*; pub use mesh::*;
pub use primitives::*; 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_app::{App, Plugin};
use bevy_asset::{AssetApp, Handle}; 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. /// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU.
pub struct MeshPlugin; pub struct MeshPlugin;
@ -27,5 +32,58 @@ impl Plugin for MeshPlugin {
.register_type::<Vec<Entity>>() .register_type::<Vec<Entity>>()
// 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready // 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
.add_plugins(RenderAssetPlugin::<Mesh, Image>::default()); .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);
} }
} }

View File

@ -1,16 +1,14 @@
use crate::mesh::MeshVertexBufferLayoutRef;
use crate::render_resource::CachedComputePipelineId; use crate::render_resource::CachedComputePipelineId;
use crate::{ use crate::{
mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout, MissingVertexAttributeError}, mesh::MissingVertexAttributeError,
render_resource::{ render_resource::{
CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor, CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor,
VertexBufferLayout, VertexBufferLayout,
}, },
}; };
use bevy_ecs::system::Resource; use bevy_ecs::system::Resource;
use bevy_utils::{ use bevy_utils::{default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap};
default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap,
PreHashMapExt,
};
use std::{fmt::Debug, hash::Hash}; use std::{fmt::Debug, hash::Hash};
use thiserror::Error; use thiserror::Error;
@ -79,14 +77,13 @@ pub trait SpecializedMeshPipeline {
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>; ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>;
} }
#[derive(Resource)] #[derive(Resource)]
pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> { pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
mesh_layout_cache: mesh_layout_cache: HashMap<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>,
PreHashMap<InnerMeshVertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>,
vertex_layout_cache: HashMap<VertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>, vertex_layout_cache: HashMap<VertexBufferLayout, HashMap<S::Key, CachedRenderPipelineId>>,
} }
@ -106,12 +103,9 @@ impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
cache: &PipelineCache, cache: &PipelineCache,
specialize_pipeline: &S, specialize_pipeline: &S,
key: S::Key, key: S::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> { ) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
let map = self match self.mesh_layout_cache.entry((layout.clone(), key.clone())) {
.mesh_layout_cache
.get_or_insert_with(layout, Default::default);
match map.entry(key.clone()) {
Entry::Occupied(entry) => Ok(*entry.into_mut()), Entry::Occupied(entry) => Ok(*entry.into_mut()),
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let descriptor = specialize_pipeline let descriptor = specialize_pipeline

View File

@ -12,7 +12,7 @@ use bevy_ecs::{
}; };
use bevy_log::error; use bevy_log::error;
use bevy_render::{ use bevy_render::{
mesh::{Mesh, MeshVertexBufferLayout}, mesh::{Mesh, MeshVertexBufferLayoutRef},
prelude::Image, prelude::Image,
render_asset::{prepare_assets, RenderAssets}, render_asset::{prepare_assets, RenderAssets},
render_phase::{ render_phase::{
@ -125,7 +125,7 @@ pub trait Material2d: AsBindGroup + Asset + Clone + Sized {
#[inline] #[inline]
fn specialize( fn specialize(
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
key: Material2dKey<Self>, key: Material2dKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
Ok(()) Ok(())
@ -271,7 +271,7 @@ where
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh2d_pipeline.specialize(key.mesh_key, layout)?; let mut descriptor = self.mesh2d_pipeline.specialize(key.mesh_key, layout)?;
if let Some(vertex_shader) = &self.vertex_shader { if let Some(vertex_shader) = &self.vertex_shader {

View File

@ -11,13 +11,14 @@ use bevy_ecs::{
}; };
use bevy_math::{Affine3, Vec4}; use bevy_math::{Affine3, Vec4};
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::mesh::MeshVertexBufferLayoutRef;
use bevy_render::{ use bevy_render::{
batching::{ batching::{
batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData, batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData,
NoAutomaticBatching, NoAutomaticBatching,
}, },
globals::{GlobalsBuffer, GlobalsUniform}, globals::{GlobalsBuffer, GlobalsUniform},
mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout}, mesh::{GpuBufferInfo, Mesh},
render_asset::RenderAssets, render_asset::RenderAssets,
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass}, render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::{binding_types::uniform_buffer, *}, render_resource::{binding_types::uniform_buffer, *},
@ -436,32 +437,32 @@ impl SpecializedMeshPipeline for Mesh2dPipeline {
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut shader_defs = Vec::new(); let mut shader_defs = Vec::new();
let mut vertex_attributes = 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()); shader_defs.push("VERTEX_POSITIONS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0)); 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()); shader_defs.push("VERTEX_NORMALS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1)); 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()); shader_defs.push("VERTEX_UVS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2)); 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()); shader_defs.push("VERTEX_TANGENTS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3)); 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()); shader_defs.push("VERTEX_COLORS".into());
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4)); 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) { let format = match key.contains(Mesh2dPipelineKey::HDR) {
true => ViewTarget::TEXTURE_FORMAT_HDR, true => ViewTarget::TEXTURE_FORMAT_HDR,

View File

@ -5,7 +5,7 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
render::{ render::{
mesh::{MeshVertexAttribute, MeshVertexBufferLayout}, mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef},
render_resource::*, render_resource::*,
}, },
sprite::{Material2d, Material2dKey, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle}, sprite::{Material2d, Material2dKey, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle},
@ -70,10 +70,10 @@ impl Material2d for CustomMaterial {
fn specialize( fn specialize(
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
_key: Material2dKey<Self>, _key: Material2dKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
let vertex_layout = layout.get_layout(&[ let vertex_layout = layout.0.get_layout(&[
Mesh::ATTRIBUTE_POSITION.at_shader_location(0), Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
Mesh::ATTRIBUTE_COLOR.at_shader_location(1), Mesh::ATTRIBUTE_COLOR.at_shader_location(1),
ATTRIBUTE_BARYCENTRIC.at_shader_location(2), ATTRIBUTE_BARYCENTRIC.at_shader_location(2),

View File

@ -5,7 +5,7 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
render::{ render::{
mesh::{MeshVertexBufferLayout, PrimitiveTopology}, mesh::{MeshVertexBufferLayoutRef, PrimitiveTopology},
render_asset::RenderAssetUsages, render_asset::RenderAssetUsages,
render_resource::{ render_resource::{
AsBindGroup, PolygonMode, RenderPipelineDescriptor, ShaderRef, AsBindGroup, PolygonMode, RenderPipelineDescriptor, ShaderRef,
@ -78,7 +78,7 @@ impl Material for LineMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout, _layout: &MeshVertexBufferLayoutRef,
_key: MaterialPipelineKey<Self>, _key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
// This is the important part to tell bevy to render this material as a line between vertices // This is the important part to tell bevy to render this material as a line between vertices

View File

@ -5,7 +5,7 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
render::{ render::{
mesh::{MeshVertexAttribute, MeshVertexBufferLayout}, mesh::{MeshVertexAttribute, MeshVertexBufferLayoutRef},
render_resource::{ render_resource::{
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
VertexFormat, VertexFormat,
@ -74,10 +74,10 @@ impl Material for CustomMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
_key: MaterialPipelineKey<Self>, _key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
let vertex_layout = layout.get_layout(&[ let vertex_layout = layout.0.get_layout(&[
Mesh::ATTRIBUTE_POSITION.at_shader_location(0), Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
ATTRIBUTE_BLEND_COLOR.at_shader_location(1), ATTRIBUTE_BLEND_COLOR.at_shader_location(1),
])?; ])?;

View File

@ -5,7 +5,7 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
render::{ render::{
mesh::MeshVertexBufferLayout, mesh::MeshVertexBufferLayoutRef,
render_resource::{ render_resource::{
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
}, },
@ -62,7 +62,7 @@ impl Material for CustomMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout, _layout: &MeshVertexBufferLayoutRef,
key: MaterialPipelineKey<Self>, key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
if key.bind_group_data.is_red { if key.bind_group_data.is_red {

View File

@ -12,7 +12,7 @@ use bevy::{
prelude::*, prelude::*,
render::{ render::{
extract_component::{ExtractComponent, ExtractComponentPlugin}, extract_component::{ExtractComponent, ExtractComponentPlugin},
mesh::{GpuBufferInfo, MeshVertexBufferLayout}, mesh::{GpuBufferInfo, MeshVertexBufferLayoutRef},
render_asset::RenderAssets, render_asset::RenderAssets,
render_phase::{ render_phase::{
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
@ -197,7 +197,7 @@ impl SpecializedMeshPipeline for CustomPipeline {
fn specialize( fn specialize(
&self, &self,
key: Self::Key, key: Self::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayoutRef,
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> { ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
let mut descriptor = self.mesh_pipeline.specialize(key, layout)?; let mut descriptor = self.mesh_pipeline.specialize(key, layout)?;

View File

@ -5,7 +5,7 @@ use bevy::{
prelude::*, prelude::*,
reflect::TypePath, reflect::TypePath,
render::{ render::{
mesh::MeshVertexBufferLayout, mesh::MeshVertexBufferLayoutRef,
render_resource::{ render_resource::{
AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError, AsBindGroup, RenderPipelineDescriptor, ShaderRef, SpecializedMeshPipelineError,
}, },
@ -78,7 +78,7 @@ impl Material for CustomMaterial {
fn specialize( fn specialize(
_pipeline: &MaterialPipeline<Self>, _pipeline: &MaterialPipeline<Self>,
descriptor: &mut RenderPipelineDescriptor, descriptor: &mut RenderPipelineDescriptor,
_layout: &MeshVertexBufferLayout, _layout: &MeshVertexBufferLayoutRef,
_key: MaterialPipelineKey<Self>, _key: MaterialPipelineKey<Self>,
) -> Result<(), SpecializedMeshPipelineError> { ) -> Result<(), SpecializedMeshPipelineError> {
descriptor.vertex.entry_point = "main".into(); descriptor.vertex.entry_point = "main".into();

View File

@ -18,8 +18,9 @@ use bevy::{
render::{ render::{
render_asset::RenderAssetUsages, render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat}, render_resource::{Extent3d, TextureDimension, TextureFormat},
view::NoFrustumCulling,
}, },
window::{PresentMode, WindowPlugin, WindowResolution}, window::{PresentMode, WindowResolution},
winit::{UpdateMode, WinitSettings}, winit::{UpdateMode, WinitSettings},
}; };
use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng}; 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. /// the number of different textures from which to randomly select the material base color. 0 means no textures.
#[argh(option, default = "0")] #[argh(option, default = "0")]
material_texture_count: usize, material_texture_count: usize,
/// whether to disable frustum culling, for stress testing purposes
#[argh(switch)]
no_frustum_culling: bool,
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
@ -131,12 +136,15 @@ fn setup(
let spherical_polar_theta_phi = let spherical_polar_theta_phi =
fibonacci_spiral_on_sphere(golden_ratio, i, N_POINTS); fibonacci_spiral_on_sphere(golden_ratio, i, N_POINTS);
let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi); let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
commands.spawn(PbrBundle { let mut cube = commands.spawn(PbrBundle {
mesh: mesh.clone(), mesh: mesh.clone(),
material: materials.choose(&mut material_rng).unwrap().clone(), material: materials.choose(&mut material_rng).unwrap().clone(),
transform: Transform::from_translation((radius * unit_sphere_p).as_vec3()), transform: Transform::from_translation((radius * unit_sphere_p).as_vec3()),
..default() ..default()
}); });
if args.no_frustum_culling {
cube.insert(NoFrustumCulling);
}
} }
// camera // camera