diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 6944aeb5d7..d75be9f3c9 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -50,6 +50,7 @@ serde = { version = "1", features = ["derive", "rc"] } bincode = "1" range-alloc = "0.1" nonmax = "0.5" +static_assertions = "1" [lints] workspace = true diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 5b758265f6..4951d06eec 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -661,25 +661,9 @@ pub fn queue_material_meshes( continue; }; - let forward = match material.properties.render_method { - OpaqueRendererMethod::Forward => true, - OpaqueRendererMethod::Deferred => false, - OpaqueRendererMethod::Auto => unreachable!(), - }; - - let mut mesh_key = view_key; - - mesh_key |= MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - - if mesh.morph_targets.is_some() { - mesh_key |= MeshPipelineKey::MORPH_TARGETS; - } - - if material.properties.reads_view_transmission_texture { - mesh_key |= MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE; - } - - mesh_key |= alpha_mode_pipeline_key(material.properties.alpha_mode); + let mut mesh_key = view_key + | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits()) + | material.properties.mesh_pipeline_key_bits; let lightmap_image = render_lightmaps .render_lightmaps @@ -724,7 +708,7 @@ pub fn queue_material_meshes( batch_range: 0..1, dynamic_offset: None, }); - } else if forward { + } else if material.properties.render_method == OpaqueRendererMethod::Forward { let bin_key = Opaque3dBinKey { draw_function: draw_opaque_pbr, pipeline: pipeline_id, @@ -748,7 +732,7 @@ pub fn queue_material_meshes( batch_range: 0..1, dynamic_offset: None, }); - } else if forward { + } else if material.properties.render_method == OpaqueRendererMethod::Forward { let bin_key = OpaqueNoLightmap3dBinKey { draw_function: draw_alpha_mask_pbr, pipeline: pipeline_id, @@ -823,7 +807,7 @@ impl DefaultOpaqueRendererMethod { /// bandwidth usage which can be unsuitable for low end mobile or other bandwidth-constrained devices. /// /// If a material indicates `OpaqueRendererMethod::Auto`, `DefaultOpaqueRendererMethod` will be used. -#[derive(Default, Clone, Copy, Debug, Reflect)] +#[derive(Default, Clone, Copy, Debug, PartialEq, Reflect)] pub enum OpaqueRendererMethod { #[default] Forward, @@ -838,6 +822,11 @@ pub struct MaterialProperties { pub render_method: OpaqueRendererMethod, /// The [`AlphaMode`] of this material. pub alpha_mode: AlphaMode, + /// The bits in the [`MeshPipelineKey`] for this material. + /// + /// These are precalculated so that we can just "or" them together in + /// [`queue_material_meshes`]. + pub mesh_pipeline_key_bits: MeshPipelineKey, /// Add a bias to the view depth of the mesh which can be used to force a specific render order /// for meshes with equal depth, to avoid z-fighting. /// The bias is in depth-texture units so large values may be needed to overcome small depth differences. @@ -1061,6 +1050,14 @@ fn prepare_material( OpaqueRendererMethod::Deferred => OpaqueRendererMethod::Deferred, OpaqueRendererMethod::Auto => default_opaque_render_method, }; + + let mut mesh_pipeline_key_bits = MeshPipelineKey::empty(); + mesh_pipeline_key_bits.set( + MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE, + material.reads_view_transmission_texture(), + ); + mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode())); + Ok(PreparedMaterial { bindings: prepared.bindings, bind_group: prepared.bind_group, @@ -1068,8 +1065,10 @@ fn prepare_material( properties: MaterialProperties { alpha_mode: material.alpha_mode(), depth_bias: material.depth_bias(), - reads_view_transmission_texture: material.reads_view_transmission_texture(), + reads_view_transmission_texture: mesh_pipeline_key_bits + .contains(MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE), render_method: method, + mesh_pipeline_key_bits, }, }) } diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index b6df62aaac..0536434c25 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -792,11 +792,8 @@ pub fn queue_prepass_material_meshes( continue; }; - let mut mesh_key = - MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) | view_key; - if mesh.morph_targets.is_some() { - mesh_key |= MeshPipelineKey::MORPH_TARGETS; - } + let mut mesh_key = view_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits()); + let alpha_mode = material.properties.alpha_mode; match alpha_mode { AlphaMode::Opaque => {} diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 2dacc9943c..e616ee250c 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1644,6 +1644,10 @@ pub fn queue_shadows( }; // NOTE: Lights with shadow mapping disabled will have no visible entities // so no meshes will be queued + + let mut light_key = MeshPipelineKey::DEPTH_PREPASS; + light_key.set(MeshPipelineKey::DEPTH_CLAMP_ORTHO, is_directional_light); + for entity in visible_entities.iter().copied() { let Some(mesh_instance) = render_mesh_instances.get(&entity) else { continue; @@ -1662,14 +1666,7 @@ pub fn queue_shadows( }; let mut mesh_key = - MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) - | MeshPipelineKey::DEPTH_PREPASS; - if mesh.morph_targets.is_some() { - mesh_key |= MeshPipelineKey::MORPH_TARGETS; - } - if is_directional_light { - mesh_key |= MeshPipelineKey::DEPTH_CLAMP_ORTHO; - } + light_key | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits()); // Even though we don't use the lightmap in the shadow map, the // `SetMeshBindGroup` render command will bind the data for it. So diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index f6d512625b..6ba0bf5528 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -31,6 +31,7 @@ use bevy_utils::{tracing::error, Entry, HashMap, Parallel}; #[cfg(debug_assertions)] use bevy_utils::warn_once; +use static_assertions::const_assert_eq; use crate::render::{ morph::{ @@ -508,7 +509,13 @@ bitflags::bitflags! { // NOTE: Apparently quadro drivers support up to 64x MSAA. /// MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA. pub struct MeshPipelineKey: u32 { + // Nothing const NONE = 0; + + // Inherited bits + const MORPH_TARGETS = BaseMeshPipelineKey::MORPH_TARGETS.bits(); + + // Flag bits const HDR = 1 << 0; const TONEMAP_IN_SHADER = 1 << 1; const DEBAND_DITHER = 1 << 2; @@ -522,17 +529,18 @@ bitflags::bitflags! { const SCREEN_SPACE_AMBIENT_OCCLUSION = 1 << 9; const DEPTH_CLAMP_ORTHO = 1 << 10; const TEMPORAL_JITTER = 1 << 11; - const MORPH_TARGETS = 1 << 12; - const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 13; - const LIGHTMAPPED = 1 << 14; - const IRRADIANCE_VOLUME = 1 << 15; + const READS_VIEW_TRANSMISSION_TEXTURE = 1 << 12; + const LIGHTMAPPED = 1 << 13; + const IRRADIANCE_VOLUME = 1 << 14; + const LAST_FLAG = Self::IRRADIANCE_VOLUME.bits(); + + // Bitfields const BLEND_RESERVED_BITS = Self::BLEND_MASK_BITS << Self::BLEND_SHIFT_BITS; // ← Bitmask reserving bits for the blend state const BLEND_OPAQUE = 0 << Self::BLEND_SHIFT_BITS; // ← Values are just sequential within the mask, and can range from 0 to 3 const BLEND_PREMULTIPLIED_ALPHA = 1 << Self::BLEND_SHIFT_BITS; // const BLEND_MULTIPLY = 2 << Self::BLEND_SHIFT_BITS; // ← We still have room for one more value without adding more bits const BLEND_ALPHA = 3 << Self::BLEND_SHIFT_BITS; const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; - const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; const TONEMAP_METHOD_RESERVED_BITS = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS; const TONEMAP_METHOD_NONE = 0 << Self::TONEMAP_METHOD_SHIFT_BITS; const TONEMAP_METHOD_REINHARD = 1 << Self::TONEMAP_METHOD_SHIFT_BITS; @@ -556,36 +564,38 @@ bitflags::bitflags! { const SCREEN_SPACE_SPECULAR_TRANSMISSION_MEDIUM = 1 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS; const SCREEN_SPACE_SPECULAR_TRANSMISSION_HIGH = 2 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS; const SCREEN_SPACE_SPECULAR_TRANSMISSION_ULTRA = 3 << Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS; + const ALL_RESERVED_BITS = + Self::BLEND_RESERVED_BITS.bits() | + Self::MSAA_RESERVED_BITS.bits() | + Self::TONEMAP_METHOD_RESERVED_BITS.bits() | + Self::SHADOW_FILTER_METHOD_RESERVED_BITS.bits() | + Self::VIEW_PROJECTION_RESERVED_BITS.bits() | + Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_RESERVED_BITS.bits(); } } impl MeshPipelineKey { const MSAA_MASK_BITS: u32 = 0b111; - const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones(); - - const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111; - const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = - Self::MSAA_SHIFT_BITS - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones(); + const MSAA_SHIFT_BITS: u32 = Self::LAST_FLAG.bits().trailing_zeros(); const BLEND_MASK_BITS: u32 = 0b11; - const BLEND_SHIFT_BITS: u32 = - Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS - Self::BLEND_MASK_BITS.count_ones(); + const BLEND_SHIFT_BITS: u32 = Self::MSAA_MASK_BITS.count_ones() + Self::MSAA_SHIFT_BITS; const TONEMAP_METHOD_MASK_BITS: u32 = 0b111; const TONEMAP_METHOD_SHIFT_BITS: u32 = - Self::BLEND_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones(); + Self::BLEND_MASK_BITS.count_ones() + Self::BLEND_SHIFT_BITS; const SHADOW_FILTER_METHOD_MASK_BITS: u32 = 0b11; const SHADOW_FILTER_METHOD_SHIFT_BITS: u32 = - Self::TONEMAP_METHOD_SHIFT_BITS - Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones(); + Self::TONEMAP_METHOD_MASK_BITS.count_ones() + Self::TONEMAP_METHOD_SHIFT_BITS; const VIEW_PROJECTION_MASK_BITS: u32 = 0b11; const VIEW_PROJECTION_SHIFT_BITS: u32 = - Self::SHADOW_FILTER_METHOD_SHIFT_BITS - Self::VIEW_PROJECTION_MASK_BITS.count_ones(); + Self::SHADOW_FILTER_METHOD_MASK_BITS.count_ones() + Self::SHADOW_FILTER_METHOD_SHIFT_BITS; const SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS: u32 = 0b11; - const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u32 = Self::VIEW_PROJECTION_SHIFT_BITS - - Self::SCREEN_SPACE_SPECULAR_TRANSMISSION_MASK_BITS.count_ones(); + const SCREEN_SPACE_SPECULAR_TRANSMISSION_SHIFT_BITS: u32 = + Self::VIEW_PROJECTION_MASK_BITS.count_ones() + Self::VIEW_PROJECTION_SHIFT_BITS; pub fn from_msaa_samples(msaa_samples: u32) -> Self { let msaa_bits = @@ -607,14 +617,15 @@ impl MeshPipelineKey { pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self { let primitive_topology_bits = ((primitive_topology as u32) - & Self::PRIMITIVE_TOPOLOGY_MASK_BITS) - << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; + & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS) + << BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; Self::from_bits_retain(primitive_topology_bits) } pub fn primitive_topology(&self) -> PrimitiveTopology { - let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS) - & Self::PRIMITIVE_TOPOLOGY_MASK_BITS; + let primitive_topology_bits = (self.bits() + >> BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS) + & BaseMeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS; match primitive_topology_bits { x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList, x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList, @@ -626,6 +637,13 @@ impl MeshPipelineKey { } } +// Ensure that we didn't overflow the number of bits available in `MeshPipelineKey`. +const_assert_eq!( + (((MeshPipelineKey::LAST_FLAG.bits() << 1) - 1) | MeshPipelineKey::ALL_RESERVED_BITS.bits()) + & BaseMeshPipelineKey::all().bits(), + 0 +); + fn is_skinned(layout: &MeshVertexBufferLayoutRef) -> bool { layout.0.contains(Mesh::ATTRIBUTE_JOINT_INDEX) && layout.0.contains(Mesh::ATTRIBUTE_JOINT_WEIGHT) diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index e5f355bc51..f1c229f5f4 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -1,6 +1,7 @@ mod conversions; pub mod skinning; use bevy_transform::components::Transform; +use bitflags::bitflags; pub use wgpu::PrimitiveTopology; use crate::{ @@ -1393,6 +1394,43 @@ impl From<&Indices> for IndexFormat { } } +bitflags! { + /// Our base mesh pipeline key bits start from the highest bit and go + /// downward. The PBR mesh pipeline key bits start from the lowest bit and + /// go upward. This allows the PBR bits in the downstream crate `bevy_pbr` + /// to coexist in the same field without any shifts. + #[derive(Clone, Debug)] + pub struct BaseMeshPipelineKey: u32 { + const MORPH_TARGETS = 1 << 31; + } +} + +impl BaseMeshPipelineKey { + pub const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111; + pub const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = + 31 - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones(); + + pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self { + let primitive_topology_bits = ((primitive_topology as u32) + & Self::PRIMITIVE_TOPOLOGY_MASK_BITS) + << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS; + Self::from_bits_retain(primitive_topology_bits) + } + + pub fn primitive_topology(&self) -> PrimitiveTopology { + let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS) + & Self::PRIMITIVE_TOPOLOGY_MASK_BITS; + match primitive_topology_bits { + x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList, + x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList, + x if x == PrimitiveTopology::LineStrip as u32 => PrimitiveTopology::LineStrip, + x if x == PrimitiveTopology::TriangleList as u32 => PrimitiveTopology::TriangleList, + x if x == PrimitiveTopology::TriangleStrip as u32 => PrimitiveTopology::TriangleStrip, + _ => PrimitiveTopology::default(), + } + } +} + /// The GPU-representation of a [`Mesh`]. /// Consists of a vertex data buffer and an optional index data buffer. #[derive(Debug, Clone)] @@ -1402,10 +1440,17 @@ pub struct GpuMesh { pub vertex_count: u32, pub morph_targets: Option, pub buffer_info: GpuBufferInfo, - pub primitive_topology: PrimitiveTopology, + pub key_bits: BaseMeshPipelineKey, pub layout: MeshVertexBufferLayoutRef, } +impl GpuMesh { + #[inline] + pub fn primitive_topology(&self) -> PrimitiveTopology { + self.key_bits.primitive_topology() + } +} + /// The index/vertex buffer info of a [`GpuMesh`]. #[derive(Debug, Clone)] pub enum GpuBufferInfo { @@ -1461,11 +1506,17 @@ impl RenderAsset for Mesh { let mesh_vertex_buffer_layout = self.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts); + let mut key_bits = BaseMeshPipelineKey::from_primitive_topology(self.primitive_topology()); + key_bits.set( + BaseMeshPipelineKey::MORPH_TARGETS, + self.morph_targets.is_some(), + ); + Ok(GpuMesh { vertex_buffer, vertex_count: self.count_vertices() as u32, buffer_info, - primitive_topology: self.primitive_topology(), + key_bits, layout: mesh_vertex_buffer_layout, morph_targets: self .morph_targets diff --git a/crates/bevy_render/src/render_resource/pipeline_specializer.rs b/crates/bevy_render/src/render_resource/pipeline_specializer.rs index 746f7bee7a..93c4b236c6 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -8,6 +8,7 @@ use crate::{ }, }; use bevy_ecs::system::Resource; +use bevy_utils::hashbrown::hash_map::VacantEntry; use bevy_utils::{default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap}; use std::{fmt::Debug, hash::Hash}; use thiserror::Error; @@ -84,9 +85,14 @@ pub trait SpecializedMeshPipeline { #[derive(Resource)] pub struct SpecializedMeshPipelines { mesh_layout_cache: HashMap<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>, - vertex_layout_cache: HashMap>, + vertex_layout_cache: VertexLayoutCache, } +pub type VertexLayoutCache = HashMap< + VertexBufferLayout, + HashMap<::Key, CachedRenderPipelineId>, +>; + impl Default for SpecializedMeshPipelines { fn default() -> Self { Self { @@ -105,55 +111,72 @@ impl SpecializedMeshPipelines { key: S::Key, layout: &MeshVertexBufferLayoutRef, ) -> Result { - match self.mesh_layout_cache.entry((layout.clone(), key.clone())) { + return match self.mesh_layout_cache.entry((layout.clone(), key.clone())) { Entry::Occupied(entry) => Ok(*entry.into_mut()), - Entry::Vacant(entry) => { - let descriptor = specialize_pipeline - .specialize(key.clone(), layout) - .map_err(|mut err| { - { - let SpecializedMeshPipelineError::MissingVertexAttribute(err) = - &mut err; - err.pipeline_type = Some(std::any::type_name::()); - } - err - })?; - // Different MeshVertexBufferLayouts can produce the same final VertexBufferLayout - // We want compatible vertex buffer layouts to use the same pipelines, so we must "deduplicate" them - let layout_map = match self - .vertex_layout_cache - .raw_entry_mut() - .from_key(&descriptor.vertex.buffers[0]) - { - RawEntryMut::Occupied(entry) => entry.into_mut(), - RawEntryMut::Vacant(entry) => { - entry - .insert(descriptor.vertex.buffers[0].clone(), Default::default()) - .1 + Entry::Vacant(entry) => specialize_slow( + &mut self.vertex_layout_cache, + cache, + specialize_pipeline, + key, + layout, + entry, + ), + }; + + #[cold] + fn specialize_slow( + vertex_layout_cache: &mut VertexLayoutCache, + cache: &PipelineCache, + specialize_pipeline: &S, + key: S::Key, + layout: &MeshVertexBufferLayoutRef, + entry: VacantEntry<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>, + ) -> Result + where + S: SpecializedMeshPipeline, + { + let descriptor = specialize_pipeline + .specialize(key.clone(), layout) + .map_err(|mut err| { + { + let SpecializedMeshPipelineError::MissingVertexAttribute(err) = &mut err; + err.pipeline_type = Some(std::any::type_name::()); } - }; - Ok(*entry.insert(match layout_map.entry(key) { - Entry::Occupied(entry) => { - if cfg!(debug_assertions) { - let stored_descriptor = - cache.get_render_pipeline_descriptor(*entry.get()); - if stored_descriptor != &descriptor { - error!( - "The cached pipeline descriptor for {} is not \ - equal to the generated descriptor for the given key. \ - This means the SpecializePipeline implementation uses \ - unused' MeshVertexBufferLayout information to specialize \ - the pipeline. This is not allowed because it would invalidate \ - the pipeline cache.", - std::any::type_name::() - ); - } + err + })?; + // Different MeshVertexBufferLayouts can produce the same final VertexBufferLayout + // We want compatible vertex buffer layouts to use the same pipelines, so we must "deduplicate" them + let layout_map = match vertex_layout_cache + .raw_entry_mut() + .from_key(&descriptor.vertex.buffers[0]) + { + RawEntryMut::Occupied(entry) => entry.into_mut(), + RawEntryMut::Vacant(entry) => { + entry + .insert(descriptor.vertex.buffers[0].clone(), Default::default()) + .1 + } + }; + Ok(*entry.insert(match layout_map.entry(key) { + Entry::Occupied(entry) => { + if cfg!(debug_assertions) { + let stored_descriptor = cache.get_render_pipeline_descriptor(*entry.get()); + if stored_descriptor != &descriptor { + error!( + "The cached pipeline descriptor for {} is not \ + equal to the generated descriptor for the given key. \ + This means the SpecializePipeline implementation uses \ + unused' MeshVertexBufferLayout information to specialize \ + the pipeline. This is not allowed because it would invalidate \ + the pipeline cache.", + std::any::type_name::() + ); } - *entry.into_mut() } - Entry::Vacant(entry) => *entry.insert(cache.queue_render_pipeline(descriptor)), - })) - } + *entry.into_mut() + } + Entry::Vacant(entry) => *entry.insert(cache.queue_render_pipeline(descriptor)), + })) } } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 4481dc2bb5..ad05b7fe61 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -426,7 +426,7 @@ pub fn queue_material2d_meshes( continue; }; let mesh_key = - view_key | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); + view_key | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology()); let pipeline_id = pipelines.specialize( &pipeline_cache, diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 860bea5d60..d8a5524aa8 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -383,7 +383,7 @@ pub fn queue_colored_mesh2d( let mut mesh2d_key = mesh_key; if let Some(mesh) = render_meshes.get(mesh2d_handle) { mesh2d_key |= - Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); + Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology()); } let pipeline_id = diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 575f287962..44a01c7584 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -133,7 +133,8 @@ fn queue_custom( let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id) else { continue; }; - let key = view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let key = + view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology()); let pipeline = pipelines .specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout) .unwrap();