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_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

View File

@ -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;
}

View File

@ -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 {

View File

@ -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() {

View File

@ -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![

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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,

View File

@ -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),

View File

@ -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

View File

@ -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),
])?;

View File

@ -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 {

View File

@ -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)?;

View File

@ -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();

View File

@ -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