diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 1e85d483fd4d1..5b8cca3b9b7e1 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -125,33 +125,33 @@ async fn load_gltf<'a, 'b>( .read_positions() .map(|v| VertexAttributeValues::Float32x3(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute); } if let Some(vertex_attribute) = reader .read_normals() .map(|v| VertexAttributeValues::Float32x3(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute); } if let Some(vertex_attribute) = reader .read_tangents() .map(|v| VertexAttributeValues::Float32x4(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute); + mesh.insert_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute); } if let Some(vertex_attribute) = reader .read_tex_coords(0) .map(|v| VertexAttributeValues::Float32x2(v.into_f32().collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute); } else { let len = mesh.count_vertices(); let uvs = vec![[0.0, 0.0]; len]; bevy_log::debug!("missing `TEXCOORD_0` vertex attribute, loading zeroed out UVs"); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); } // if let Some(vertex_attribute) = reader diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 8325030519b98..8b5d4ad8e50a6 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -40,7 +40,7 @@ use bevy_render::{ prelude::Color, render_graph::RenderGraph, render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, - render_resource::{Shader, SpecializedPipelines}, + render_resource::{Shader, SpecializedMeshPipelines}, view::VisibilitySystems, RenderApp, RenderStage, }; @@ -178,7 +178,7 @@ impl Plugin for PbrPlugin { .init_resource::>() .init_resource::() .init_resource::() - .init_resource::>(); + .init_resource::>(); let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); render_app.add_render_command::(); diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d9ee95446a887..e63fdfd54698d 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -15,7 +15,7 @@ use bevy_ecs::{ world::FromWorld, }; use bevy_render::{ - mesh::Mesh, + mesh::{Mesh, MeshVertexBufferLayout}, render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets}, render_component::ExtractComponentPlugin, render_phase::{ @@ -24,7 +24,7 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + SpecializedMeshPipeline, SpecializedMeshPipelines, }, renderer::RenderDevice, view::{ExtractedView, Msaa, VisibleEntities}, @@ -81,7 +81,12 @@ impl SpecializedMaterial for M { fn key(_material: &::PreparedAsset) -> Self::Key {} #[inline] - fn specialize(_key: Self::Key, _descriptor: &mut RenderPipelineDescriptor) {} + fn specialize( + _descriptor: &mut RenderPipelineDescriptor, + _key: Self::Key, + _layout: &MeshVertexBufferLayout, + ) { + } #[inline] fn bind_group(material: &::PreparedAsset) -> &BindGroup { @@ -130,7 +135,11 @@ pub trait SpecializedMaterial: Asset + RenderAsset { fn key(material: &::PreparedAsset) -> Self::Key; /// Specializes the given `descriptor` according to the given `key`. - fn specialize(key: Self::Key, descriptor: &mut RenderPipelineDescriptor); + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ); /// Returns this material's [`BindGroup`]. This should match the layout returned by [`SpecializedMaterial::bind_group_layout`]. fn bind_group(material: &::PreparedAsset) -> &BindGroup; @@ -188,7 +197,7 @@ impl Plugin for MaterialPlugin { .add_render_command::>() .add_render_command::>() .init_resource::>() - .init_resource::>>() + .init_resource::>>() .add_system_to_stage(RenderStage::Queue, queue_material_meshes::); } } @@ -202,11 +211,15 @@ pub struct MaterialPipeline { marker: PhantomData, } -impl SpecializedPipeline for MaterialPipeline { +impl SpecializedMeshPipeline for MaterialPipeline { type Key = (MeshPipelineKey, M::Key); - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh_pipeline.specialize(key.0); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut descriptor = self.mesh_pipeline.specialize(key.0, layout); if let Some(vertex_shader) = &self.vertex_shader { descriptor.vertex.shader = vertex_shader.clone(); } @@ -220,7 +233,7 @@ impl SpecializedPipeline for MaterialPipeline { self.mesh_pipeline.mesh_layout.clone(), ]); - M::specialize(key.1, &mut descriptor); + M::specialize(&mut descriptor, key.1, layout); descriptor } } @@ -275,7 +288,7 @@ pub fn queue_material_meshes( alpha_mask_draw_functions: Res>, transparent_draw_functions: Res>, material_pipeline: Res>, - mut pipelines: ResMut>>, + mut pipelines: ResMut>>, mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, @@ -307,72 +320,71 @@ pub fn queue_material_meshes( let inverse_view_matrix = view.transform.compute_matrix().inverse(); let inverse_view_row_2 = inverse_view_matrix.row(2); - let mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples); + let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for visible_entity in &visible_entities.entities { if let Ok((material_handle, mesh_handle, mesh_uniform)) = material_meshes.get(*visible_entity) { if let Some(material) = render_materials.get(material_handle) { - let mut mesh_key = mesh_key; if let Some(mesh) = render_meshes.get(mesh_handle) { - if mesh.has_tangents { - mesh_key |= MeshPipelineKey::VERTEX_TANGENTS; + let mut mesh_key = + MeshPipelineKey::from_primitive_topology(mesh.primitive_topology) + | msaa_key; + let alpha_mode = M::alpha_mode(material); + if let AlphaMode::Blend = alpha_mode { + mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS; } - mesh_key |= - MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - } - let alpha_mode = M::alpha_mode(material); - if let AlphaMode::Blend = alpha_mode { - mesh_key |= MeshPipelineKey::TRANSPARENT_MAIN_PASS; - } - let specialized_key = M::key(material); - let pipeline_id = pipelines.specialize( - &mut pipeline_cache, - &material_pipeline, - (mesh_key, specialized_key), - ); - - // NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix - // gives the z component of translation of the mesh in view space - let mesh_z = inverse_view_row_2.dot(mesh_uniform.transform.col(3)); - match alpha_mode { - AlphaMode::Opaque => { - opaque_phase.add(Opaque3d { - entity: *visible_entity, - draw_function: draw_opaque_pbr, - pipeline: pipeline_id, - // NOTE: Front-to-back ordering for opaque with ascending sort means near should have the - // lowest sort key and getting further away should increase. As we have - // -z in front of the camera, values in view space decrease away from the - // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering - distance: -mesh_z, - }); - } - AlphaMode::Mask(_) => { - alpha_mask_phase.add(AlphaMask3d { - entity: *visible_entity, - draw_function: draw_alpha_mask_pbr, - pipeline: pipeline_id, - // NOTE: Front-to-back ordering for alpha mask with ascending sort means near should have the - // lowest sort key and getting further away should increase. As we have - // -z in front of the camera, values in view space decrease away from the - // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering - distance: -mesh_z, - }); - } - AlphaMode::Blend => { - transparent_phase.add(Transparent3d { - entity: *visible_entity, - draw_function: draw_transparent_pbr, - pipeline: pipeline_id, - // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the - // lowest sort key and getting closer should increase. As we have - // -z in front of the camera, the largest distance is -far with values increasing toward the - // camera. As such we can just use mesh_z as the distance - distance: mesh_z, - }); + let specialized_key = M::key(material); + + let pipeline_id = pipelines.specialize( + &mut pipeline_cache, + &material_pipeline, + (mesh_key, specialized_key), + &mesh.layout, + ); + + // NOTE: row 2 of the inverse view matrix dotted with column 3 of the model matrix + // gives the z component of translation of the mesh in view space + let mesh_z = inverse_view_row_2.dot(mesh_uniform.transform.col(3)); + match alpha_mode { + AlphaMode::Opaque => { + opaque_phase.add(Opaque3d { + entity: *visible_entity, + draw_function: draw_opaque_pbr, + pipeline: pipeline_id, + // NOTE: Front-to-back ordering for opaque with ascending sort means near should have the + // lowest sort key and getting further away should increase. As we have + // -z in front of the camera, values in view space decrease away from the + // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering + distance: -mesh_z, + }); + } + AlphaMode::Mask(_) => { + alpha_mask_phase.add(AlphaMask3d { + entity: *visible_entity, + draw_function: draw_alpha_mask_pbr, + pipeline: pipeline_id, + // NOTE: Front-to-back ordering for alpha mask with ascending sort means near should have the + // lowest sort key and getting further away should increase. As we have + // -z in front of the camera, values in view space decrease away from the + // camera. Flipping the sign of mesh_z results in the correct front-to-back ordering + distance: -mesh_z, + }); + } + AlphaMode::Blend => { + transparent_phase.add(Transparent3d { + entity: *visible_entity, + draw_function: draw_transparent_pbr, + pipeline: pipeline_id, + // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the + // lowest sort key and getting closer should increase. As we have + // -z in front of the camera, the largest distance is -far with values increasing toward the + // camera. As such we can just use mesh_z as the distance + distance: mesh_z, + }); + } } } } diff --git a/crates/bevy_pbr/src/pbr_material.rs b/crates/bevy_pbr/src/pbr_material.rs index 6be68dfdd3e64..1e89180ec76f0 100644 --- a/crates/bevy_pbr/src/pbr_material.rs +++ b/crates/bevy_pbr/src/pbr_material.rs @@ -5,6 +5,7 @@ use bevy_math::Vec4; use bevy_reflect::TypeUuid; use bevy_render::{ color::Color, + mesh::MeshVertexBufferLayout, prelude::Shader, render_asset::{PrepareAssetError, RenderAsset, RenderAssets}, render_resource::{ @@ -338,7 +339,11 @@ impl SpecializedMaterial for StandardMaterial { } } - fn specialize(key: Self::Key, descriptor: &mut RenderPipelineDescriptor) { + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + key: Self::Key, + _layout: &MeshVertexBufferLayout, + ) { if key.normal_map { descriptor .fragment diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index cc606057f2944..7b52a61c82bfc 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,7 +14,7 @@ use bevy_math::{const_vec3, Mat4, UVec3, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles}; use bevy_render::{ camera::{Camera, CameraProjection}, color::Color, - mesh::Mesh, + mesh::{Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{ @@ -214,7 +214,6 @@ bitflags::bitflags! { #[repr(transparent)] pub struct ShadowPipelineKey: u32 { const NONE = 0; - const VERTEX_TANGENTS = (1 << 0); const PRIMITIVE_TOPOLOGY_RESERVED_BITS = ShadowPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << ShadowPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; } } @@ -244,76 +243,24 @@ impl ShadowPipelineKey { } } -impl SpecializedPipeline for ShadowPipeline { +impl SpecializedMeshPipeline for ShadowPipeline { type Key = ShadowPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let (vertex_array_stride, vertex_attributes) = - if key.contains(ShadowPipelineKey::VERTEX_TANGENTS) { - ( - 48, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 40, - shader_location: 2, - }, - // Tangent - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 24, - shader_location: 3, - }, - ], - ) - } else { - ( - 32, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 24, - shader_location: 2, - }, - ], - ) - }; + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let vertex_buffer_layout = layout + .get_layout(&[Mesh::ATTRIBUTE_POSITION.at_shader_location(0)]) + .expect("Mesh is missing a vertex attribute"); + RenderPipelineDescriptor { vertex: VertexState { shader: SHADOW_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: vec![], - buffers: vec![VertexBufferLayout { - array_stride: vertex_array_stride, - step_mode: VertexStepMode::Vertex, - attributes: vertex_attributes, - }], + buffers: vec![vertex_buffer_layout.clone()], }, fragment: None, layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]), @@ -1090,7 +1037,7 @@ pub fn queue_shadows( shadow_pipeline: Res, casting_meshes: Query<&Handle, Without>, render_meshes: Res>, - mut pipelines: ResMut>, + mut pipelines: ResMut>, mut pipeline_cache: ResMut, view_lights: Query<&ViewLightEntities>, mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase)>, @@ -1120,23 +1067,24 @@ pub fn queue_shadows( // NOTE: Lights with shadow mapping disabled will have no visible entities // so no meshes will be queued for entity in visible_entities.iter().copied() { - let mut key = ShadowPipelineKey::empty(); if let Ok(mesh_handle) = casting_meshes.get(entity) { if let Some(mesh) = render_meshes.get(mesh_handle) { - if mesh.has_tangents { - key |= ShadowPipelineKey::VERTEX_TANGENTS; - } - key |= ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); + let key = + ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline_id = pipelines.specialize( + &mut pipeline_cache, + &shadow_pipeline, + key, + &mesh.layout, + ); + + shadow_phase.add(Shadow { + draw_function: draw_shadow_mesh, + pipeline: pipeline_id, + entity, + distance: 0.0, // TODO: sort back-to-front + }); } - let pipeline_id = - pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key); - - shadow_phase.add(Shadow { - draw_function: draw_shadow_mesh, - pipeline: pipeline_id, - entity, - distance: 0.0, // TODO: sort back-to-front - }); } } } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3e8965aa28ee1..226f86407c132 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -11,7 +11,7 @@ use bevy_ecs::{ use bevy_math::{Mat4, Size}; use bevy_reflect::TypeUuid; use bevy_render::{ - mesh::{GpuBufferInfo, Mesh}, + mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, @@ -368,8 +368,7 @@ bitflags::bitflags! { /// MSAA uses the highest 6 bits for the MSAA sample count - 1 to support up to 64x MSAA. pub struct MeshPipelineKey: u32 { const NONE = 0; - const VERTEX_TANGENTS = (1 << 0); - const TRANSPARENT_MAIN_PASS = (1 << 1); + const TRANSPARENT_MAIN_PASS = (1 << 0); const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS; const PRIMITIVE_TOPOLOGY_RESERVED_BITS = MeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << MeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; } @@ -411,71 +410,30 @@ impl MeshPipelineKey { } } -impl SpecializedPipeline for MeshPipeline { +impl SpecializedMeshPipeline for MeshPipeline { type Key = MeshPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let (vertex_array_stride, vertex_attributes) = - if key.contains(MeshPipelineKey::VERTEX_TANGENTS) { - ( - 48, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 40, - shader_location: 2, - }, - // Tangent - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 24, - shader_location: 3, - }, - ], - ) - } else { - ( - 32, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 24, - shader_location: 2, - }, - ], - ) - }; + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut vertex_attributes = vec![ + Mesh::ATTRIBUTE_POSITION.at_shader_location(0), + Mesh::ATTRIBUTE_NORMAL.at_shader_location(1), + Mesh::ATTRIBUTE_UV_0.at_shader_location(2), + ]; + let mut shader_defs = Vec::new(); - if key.contains(MeshPipelineKey::VERTEX_TANGENTS) { + if layout.contains(Mesh::ATTRIBUTE_TANGENT) { shader_defs.push(String::from("VERTEX_TANGENTS")); + vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3)); } + let vertex_buffer_layout = layout + .get_layout(&vertex_attributes) + .expect("Mesh is missing a vertex attribute"); + let (label, blend, depth_write_enabled); if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) { label = "transparent_mesh_pipeline".into(); @@ -500,11 +458,7 @@ impl SpecializedPipeline for MeshPipeline { shader: MESH_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: shader_defs.clone(), - buffers: vec![VertexBufferLayout { - array_stride: vertex_array_stride, - step_mode: VertexStepMode::Vertex, - attributes: vertex_attributes, - }], + buffers: vec![vertex_buffer_layout.clone()], }, fragment: Some(FragmentState { shader: MESH_SHADER_HANDLE.typed::(), diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 9f1308179e518..bda39c0b1b283 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -5,12 +5,15 @@ use bevy_asset::{Assets, Handle, HandleUntyped}; use bevy_core_pipeline::Opaque3d; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_reflect::{Reflect, TypeUuid}; -use bevy_render::render_resource::PolygonMode; +use bevy_render::mesh::MeshVertexBufferLayout; +use bevy_render::render_resource::{ + PolygonMode, SpecializedMeshPipeline, SpecializedMeshPipelines, +}; use bevy_render::{ mesh::Mesh, render_asset::RenderAssets, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, - render_resource::{RenderPipelineCache, Shader, SpecializedPipeline, SpecializedPipelines}, + render_resource::{RenderPipelineCache, Shader}, view::{ExtractedView, Msaa}, RenderApp, RenderStage, }; @@ -35,7 +38,7 @@ impl Plugin for WireframePlugin { render_app .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_wireframes) .add_system_to_stage(RenderStage::Extract, extract_wireframe_config) .add_system_to_stage(RenderStage::Queue, queue_wireframes); @@ -79,11 +82,15 @@ impl FromWorld for WireframePipeline { } } -impl SpecializedPipeline for WireframePipeline { +impl SpecializedMeshPipeline for WireframePipeline { type Key = MeshPipelineKey; - fn specialize(&self, key: Self::Key) -> bevy_render::render_resource::RenderPipelineDescriptor { - let mut descriptor = self.mesh_pipeline.specialize(key); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> bevy_render::render_resource::RenderPipelineDescriptor { + let mut descriptor = self.mesh_pipeline.specialize(key, layout); descriptor.vertex.shader = self.shader.clone_weak(); descriptor.fragment.as_mut().unwrap().shader = self.shader.clone_weak(); descriptor.primitive.polygon_mode = PolygonMode::Line; @@ -93,13 +100,14 @@ impl SpecializedPipeline for WireframePipeline { } #[allow(clippy::too_many_arguments)] +#[allow(clippy::type_complexity)] fn queue_wireframes( opaque_3d_draw_functions: Res>, render_meshes: Res>, wireframe_config: Res, wireframe_pipeline: Res, mut pipeline_cache: ResMut, - mut specialized_pipelines: ResMut>, + mut specialized_pipelines: ResMut>, msaa: Res, mut material_meshes: QuerySet<( QueryState<(Entity, &Handle, &MeshUniform)>, @@ -111,7 +119,7 @@ fn queue_wireframes( .read() .get_id::() .unwrap(); - let key = MeshPipelineKey::from_msaa_samples(msaa.samples); + let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); @@ -119,14 +127,15 @@ fn queue_wireframes( let add_render_phase = |(entity, mesh_handle, mesh_uniform): (Entity, &Handle, &MeshUniform)| { if let Some(mesh) = render_meshes.get(mesh_handle) { - let key = - key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let key = msaa_key + | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); transparent_phase.add(Opaque3d { entity, pipeline: specialized_pipelines.specialize( &mut pipeline_cache, &wireframe_pipeline, key, + &mesh.layout, ), draw_function: draw_custom, distance: view_row_2.dot(mesh_uniform.transform.col(3)), diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index 317ce49a30902..1867577d48d43 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -3,17 +3,18 @@ mod conversions; use crate::{ primitives::Aabb, render_asset::{PrepareAssetError, RenderAsset}, - render_resource::Buffer, + render_resource::{Buffer, VertexBufferLayout}, renderer::RenderDevice, }; use bevy_core::cast_slice; use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem}; use bevy_math::*; use bevy_reflect::TypeUuid; -use bevy_utils::EnumVariantMeta; -use std::{borrow::Cow, collections::BTreeMap}; +use bevy_utils::{EnumVariantMeta, Hashed}; +use std::{collections::BTreeMap, hash::Hash}; use wgpu::{ - util::BufferInitDescriptor, BufferUsages, IndexFormat, PrimitiveTopology, VertexFormat, + util::BufferInitDescriptor, BufferUsages, IndexFormat, PrimitiveTopology, VertexAttribute, + VertexFormat, VertexStepMode, }; pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; @@ -25,10 +26,10 @@ pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; pub struct Mesh { primitive_topology: PrimitiveTopology, /// `std::collections::BTreeMap` with all defined vertex attributes (Positions, Normals, ...) - /// for this mesh. Attribute name maps to attribute values. + /// for this mesh. Attribute ids to attribute values. /// Uses a BTreeMap because, unlike HashMap, it has a defined iteration order, /// which allows easy stable VertexBuffers (i.e. same buffer order) - attributes: BTreeMap, VertexAttributeValues>, + attributes: BTreeMap, indices: Option, } @@ -50,23 +51,33 @@ pub struct Mesh { /// } /// ``` impl Mesh { - /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color"; + /// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`] + pub const ATTRIBUTE_POSITION: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Position", 0, VertexFormat::Float32x3); + /// The direction the vertex normal is facing in. /// Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal"; - /// The direction of the vertex tangent. Used for normal mapping - pub const ATTRIBUTE_TANGENT: &'static str = "Vertex_Tangent"; + pub const ATTRIBUTE_NORMAL: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3); - /// Where the vertex is located in space. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position"; /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv"; + pub const ATTRIBUTE_UV_0: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Uv", 2, VertexFormat::Float32x2); + + /// The direction of the vertex tangent. Used for normal mapping + pub const ATTRIBUTE_TANGENT: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Tangent", 3, VertexFormat::Float32x4); + + /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] + pub const ATTRIBUTE_COLOR: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Color", 4, VertexFormat::Uint32); /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_JOINT_WEIGHT: &'static str = "Vertex_JointWeight"; + pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_JointWeight", 5, VertexFormat::Float32x4); /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_JOINT_INDEX: &'static str = "Vertex_JointIndex"; + pub const ATTRIBUTE_JOINT_INDEX: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_JointIndex", 6, VertexFormat::Uint32); /// Construct a new mesh. You need to provide a [`PrimitiveTopology`] so that the /// renderer knows how to treat the vertex data. Most of the time this will be @@ -86,41 +97,62 @@ impl Mesh { /// Sets the data for a vertex attribute (position, normal etc.). The name will /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. - pub fn set_attribute( + #[inline] + pub fn insert_attribute( &mut self, - name: impl Into>, + attribute: MeshVertexAttribute, values: impl Into, ) { - let values: VertexAttributeValues = values.into(); - self.attributes.insert(name.into(), values); + self.attributes.insert( + attribute.id, + MeshAttributeData { + attribute, + values: values.into(), + }, + ); + } + + #[inline] + pub fn contains_attribute(&self, id: impl Into) -> bool { + self.attributes.contains_key(&id.into()) } /// Retrieves the data currently set to the vertex attribute with the specified `name`. - pub fn attribute(&self, name: impl Into>) -> Option<&VertexAttributeValues> { - self.attributes.get(&name.into()) + #[inline] + pub fn attribute( + &self, + id: impl Into, + ) -> Option<&VertexAttributeValues> { + self.attributes.get(&id.into()).map(|data| &data.values) } /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably. + #[inline] pub fn attribute_mut( &mut self, - name: impl Into>, + id: impl Into, ) -> Option<&mut VertexAttributeValues> { - self.attributes.get_mut(&name.into()) + self.attributes + .get_mut(&id.into()) + .map(|data| &mut data.values) } /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants /// that use triangles. + #[inline] pub fn set_indices(&mut self, indices: Option) { self.indices = indices; } /// Retrieves the vertex `indices` of the mesh. + #[inline] pub fn indices(&self) -> Option<&Indices> { self.indices.as_ref() } /// Retrieves the vertex `indices` of the mesh mutably. + #[inline] pub fn indices_mut(&mut self) -> Option<&mut Indices> { self.indices.as_mut() } @@ -134,27 +166,32 @@ impl Mesh { }) } - // pub fn get_vertex_buffer_layout(&self) -> VertexBufferLayout { - // let mut attributes = Vec::new(); - // let mut accumulated_offset = 0; - // for (attribute_name, attribute_values) in self.attributes.iter() { - // let vertex_format = VertexFormat::from(attribute_values); - // attributes.push(VertexAttribute { - // name: attribute_name.clone(), - // offset: accumulated_offset, - // format: vertex_format, - // shader_location: 0, - // }); - // accumulated_offset += vertex_format.get_size(); - // } - - // VertexBufferLayout { - // name: Default::default(), - // stride: accumulated_offset, - // step_mode: InputStepMode::Vertex, - // attributes, - // } - // } + /// For a given `descriptor` returns a [`VertexBufferLayout`] compatible with this mesh. If this + /// mesh is not compatible with the given `descriptor` (ex: it is missing vertex attributes), [`None`] will + /// be returned. + pub fn get_mesh_vertex_buffer_layout(&self) -> MeshVertexBufferLayout { + let mut attributes = Vec::with_capacity(self.attributes.len()); + let mut attribute_ids = Vec::with_capacity(self.attributes.len()); + let mut accumulated_offset = 0; + for (index, data) in self.attributes.values().enumerate() { + attribute_ids.push(data.attribute.id); + attributes.push(VertexAttribute { + offset: accumulated_offset, + format: data.attribute.format, + shader_location: index as u32, + }); + accumulated_offset += data.attribute.format.get_size(); + } + + MeshVertexBufferLayout::new(InnerMeshVertexBufferLayout { + layout: VertexBufferLayout { + array_stride: accumulated_offset, + step_mode: VertexStepMode::Vertex, + attributes, + }, + attribute_ids, + }) + } /// Counts all vertices of the mesh. /// @@ -162,11 +199,11 @@ impl Mesh { /// Panics if the attributes have different vertex counts. pub fn count_vertices(&self) -> usize { let mut vertex_count: Option = None; - for (attribute_name, attribute_data) in &self.attributes { - let attribute_len = attribute_data.len(); + for (attribute_id, attribute_data) in self.attributes.iter() { + let attribute_len = attribute_data.values.len(); if let Some(previous_vertex_count) = vertex_count { assert_eq!(previous_vertex_count, attribute_len, - "Attribute {} has a different vertex count ({}) than other attributes ({}) in this mesh.", attribute_name, attribute_len, previous_vertex_count); + "{:?} has a different vertex count ({}) than other attributes ({}) in this mesh.", attribute_id, attribute_len, previous_vertex_count); } vertex_count = Some(attribute_len); } @@ -182,8 +219,8 @@ impl Mesh { /// Panics if the attributes have different vertex counts. pub fn get_vertex_buffer_data(&self) -> Vec { let mut vertex_size = 0; - for attribute_values in self.attributes.values() { - let vertex_format = VertexFormat::from(attribute_values); + for attribute_data in self.attributes.values() { + let vertex_format = attribute_data.attribute.format; vertex_size += vertex_format.get_size() as usize; } @@ -191,10 +228,9 @@ impl Mesh { let mut attributes_interleaved_buffer = vec![0; vertex_count * vertex_size]; // bundle into interleaved buffers let mut attribute_offset = 0; - for attribute_values in self.attributes.values() { - let vertex_format = VertexFormat::from(attribute_values); - let attribute_size = vertex_format.get_size() as usize; - let attributes_bytes = attribute_values.get_bytes(); + for attribute_data in self.attributes.values() { + let attribute_size = attribute_data.attribute.format.get_size() as usize; + let attributes_bytes = attribute_data.values.get_bytes(); for (vertex_index, attribute_bytes) in attributes_bytes.chunks_exact(attribute_size).enumerate() { @@ -232,7 +268,7 @@ impl Mesh { }; for attributes in self.attributes.values_mut() { let indices = indices.iter(); - match attributes { + match &mut attributes.values { VertexAttributeValues::Float32(vec) => *vec = duplicate(vec, indices), VertexAttributeValues::Sint32(vec) => *vec = duplicate(vec, indices), VertexAttributeValues::Uint32(vec) => *vec = duplicate(vec, indices), @@ -285,7 +321,7 @@ impl Mesh { .flat_map(|normal| [normal; 3]) .collect(); - self.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + self.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); } /// Compute the Axis-Aligned Bounding Box of the mesh vertices in model space @@ -314,6 +350,118 @@ impl Mesh { } } +#[derive(Debug, Clone)] +pub struct MeshVertexAttribute { + /// The friendly name of the vertex attribute + pub name: &'static str, + + /// The _unique_ id of the vertex attribute. This will also determine sort ordering + /// when generating vertex buffers. Built-in / standard attributes will use "close to zero" + /// indices. When in doubt, use a random / very large usize to avoid conflicts. + pub id: MeshVertexAttributeId, + + /// The format of the vertex attribute. + pub format: VertexFormat, +} + +impl MeshVertexAttribute { + pub const fn new(name: &'static str, id: usize, format: VertexFormat) -> Self { + Self { + name, + id: MeshVertexAttributeId(id), + format, + } + } + + pub const fn at_shader_location(&self, shader_location: u32) -> VertexAttributeDescriptor { + VertexAttributeDescriptor::new(shader_location, self.id) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct MeshVertexAttributeId(usize); + +impl From for MeshVertexAttributeId { + fn from(attribute: MeshVertexAttribute) -> Self { + attribute.id + } +} + +pub type MeshVertexBufferLayout = Hashed; + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct InnerMeshVertexBufferLayout { + attribute_ids: Vec, + layout: VertexBufferLayout, +} + +impl InnerMeshVertexBufferLayout { + #[inline] + pub fn contains(&self, attribute_id: impl Into) -> bool { + self.attribute_ids.contains(&attribute_id.into()) + } + + // TODO: maybe these should be hidden to ensure specializers can only use this type to generate VertexBufferLayouts? + #[inline] + pub fn attribute_ids(&self) -> &[MeshVertexAttributeId] { + &self.attribute_ids + } + + #[inline] + pub fn layout(&self) -> &VertexBufferLayout { + &self.layout + } + + pub fn get_layout( + &self, + attribute_descriptors: &[VertexAttributeDescriptor], + ) -> Option { + let mut attributes = Vec::with_capacity(attribute_descriptors.len()); + for attribute_descriptor in attribute_descriptors.iter() { + if let Some(index) = self + .attribute_ids + .iter() + .position(|id| *id == attribute_descriptor.id) + { + let layout_attribute = &self.layout.attributes[index]; + attributes.push(VertexAttribute { + format: layout_attribute.format, + offset: layout_attribute.offset, + shader_location: attribute_descriptor.shader_location, + }) + } else { + return None; + } + } + + Some(VertexBufferLayout { + array_stride: self.layout.array_stride, + step_mode: self.layout.step_mode, + attributes, + }) + } +} + +pub struct VertexAttributeDescriptor { + pub shader_location: u32, + pub id: MeshVertexAttributeId, +} + +impl VertexAttributeDescriptor { + pub const fn new(shader_location: u32, id: MeshVertexAttributeId) -> Self { + Self { + shader_location, + id, + } + } +} + +#[derive(Debug, Clone)] +struct MeshAttributeData { + attribute: MeshVertexAttribute, + values: VertexAttributeValues, +} + const VEC3_MIN: Vec3 = const_vec3!([std::f32::MIN, std::f32::MIN, std::f32::MIN]); const VEC3_MAX: Vec3 = const_vec3!([std::f32::MAX, std::f32::MAX, std::f32::MAX]); @@ -521,7 +669,6 @@ impl From<&VertexAttributeValues> for VertexFormat { } } } - /// An array of indices into the [`VertexAttributeValues`] for a mesh. /// /// It describes the order in which the vertex attributes should be joined into faces. @@ -590,8 +737,8 @@ pub struct GpuMesh { /// Contains all attribute data for each vertex. pub vertex_buffer: Buffer, pub buffer_info: GpuBufferInfo, - pub has_tangents: bool, pub primitive_topology: PrimitiveTopology, + pub layout: MeshVertexBufferLayout, } /// The index/vertex buffer info of a [`GpuMesh`]. @@ -645,11 +792,13 @@ impl RenderAsset for Mesh { }, ); + let mesh_vertex_buffer_layout = mesh.get_mesh_vertex_buffer_layout(); + Ok(GpuMesh { vertex_buffer, buffer_info, - has_tangents: mesh.attributes.contains_key(Mesh::ATTRIBUTE_TANGENT), primitive_topology: mesh.primitive_topology(), + layout: mesh_vertex_buffer_layout, }) } } diff --git a/crates/bevy_render/src/mesh/shape/capsule.rs b/crates/bevy_render/src/mesh/shape/capsule.rs index 7b6d268a9a9a6..5459e8d47fe10 100644 --- a/crates/bevy_render/src/mesh/shape/capsule.rs +++ b/crates/bevy_render/src/mesh/shape/capsule.rs @@ -370,9 +370,9 @@ impl From for Mesh { assert_eq!(tris.len(), fs_len); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vs); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vns); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vts); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vs); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, vns); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, vts); mesh.set_indices(Some(Indices::U32(tris))); mesh } diff --git a/crates/bevy_render/src/mesh/shape/icosphere.rs b/crates/bevy_render/src/mesh/shape/icosphere.rs index ff2068469ec51..7b0a31962205d 100644 --- a/crates/bevy_render/src/mesh/shape/icosphere.rs +++ b/crates/bevy_render/src/mesh/shape/icosphere.rs @@ -95,9 +95,9 @@ impl From for Mesh { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, points); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, points); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } diff --git a/crates/bevy_render/src/mesh/shape/mod.rs b/crates/bevy_render/src/mesh/shape/mod.rs index 9f128d2ac4c43..31f9f8b5d4bd2 100644 --- a/crates/bevy_render/src/mesh/shape/mod.rs +++ b/crates/bevy_render/src/mesh/shape/mod.rs @@ -112,9 +112,9 @@ impl From for Mesh { ]); let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh.set_indices(Some(indices)); mesh } @@ -171,9 +171,9 @@ impl From for Mesh { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } @@ -215,9 +215,9 @@ impl From for Mesh { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } diff --git a/crates/bevy_render/src/mesh/shape/torus.rs b/crates/bevy_render/src/mesh/shape/torus.rs index abe63e00c5c27..4fdc9b13181d5 100644 --- a/crates/bevy_render/src/mesh/shape/torus.rs +++ b/crates/bevy_render/src/mesh/shape/torus.rs @@ -90,9 +90,9 @@ impl From for Mesh { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(Indices::U32(indices))); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } diff --git a/crates/bevy_render/src/mesh/shape/uvsphere.rs b/crates/bevy_render/src/mesh/shape/uvsphere.rs index 571c3123d9aa1..ccce754e564f2 100644 --- a/crates/bevy_render/src/mesh/shape/uvsphere.rs +++ b/crates/bevy_render/src/mesh/shape/uvsphere.rs @@ -82,9 +82,9 @@ impl From for Mesh { let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); mesh.set_indices(Some(Indices::U32(indices))); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vertices); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); mesh } } diff --git a/crates/bevy_render/src/render_resource/pipeline.rs b/crates/bevy_render/src/render_resource/pipeline.rs index d3bd820053fdc..b2c37825ffddc 100644 --- a/crates/bevy_render/src/render_resource/pipeline.rs +++ b/crates/bevy_render/src/render_resource/pipeline.rs @@ -4,7 +4,7 @@ use bevy_reflect::Uuid; use std::{borrow::Cow, ops::Deref, sync::Arc}; use wgpu::{ BufferAddress, ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState, - VertexAttribute, VertexStepMode, + VertexAttribute, VertexFormat, VertexStepMode, }; /// A [`RenderPipeline`] identifier. @@ -118,7 +118,7 @@ pub struct VertexState { } /// Describes how the vertex buffer is interpreted. -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Default, Clone, Debug, Hash, Eq, PartialEq)] pub struct VertexBufferLayout { /// The stride, in bytes, between elements of this buffer. pub array_stride: BufferAddress, @@ -128,6 +128,34 @@ pub struct VertexBufferLayout { pub attributes: Vec, } +impl VertexBufferLayout { + /// Creates a new densely packed [`VertexBufferLayout`] from an iterator of vertex formats. + /// Iteration order determines the `shader_location` and `offset` of the VertexAttributes. + /// The first iterated item will have a `shader_location` and `offset` of zero. + /// The `array_stride` is the sum of the size of the iterated VertexFormats (in bytes). + pub fn from_vertex_formats>( + step_mode: VertexStepMode, + vertex_formats: T, + ) -> Self { + let mut array_stride = 0; + let mut attributes = Vec::new(); + for (shader_location, format) in vertex_formats.into_iter().enumerate() { + attributes.push(VertexAttribute { + format, + offset: array_stride, + shader_location: shader_location as u32, + }); + array_stride += format.size(); + } + + VertexBufferLayout { + array_stride, + step_mode, + attributes, + } + } +} + /// Describes the fragment process in a render pipeline. #[derive(Clone, Debug)] pub struct FragmentState { diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 77ed782eb61cb..603f62c2b4d56 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -13,7 +13,7 @@ use bevy_ecs::system::{Res, ResMut}; use bevy_utils::{tracing::error, Entry, HashMap, HashSet}; use std::{hash::Hash, ops::Deref, sync::Arc}; use thiserror::Error; -use wgpu::{PipelineLayoutDescriptor, ShaderModule, VertexBufferLayout}; +use wgpu::{PipelineLayoutDescriptor, ShaderModule, VertexBufferLayout as RawVertexBufferLayout}; use super::ProcessedShader; @@ -345,7 +345,7 @@ impl RenderPipelineCache { .vertex .buffers .iter() - .map(|layout| VertexBufferLayout { + .map(|layout| RawVertexBufferLayout { array_stride: layout.array_stride, attributes: &layout.attributes, step_mode: layout.step_mode, diff --git a/crates/bevy_render/src/render_resource/pipeline_specializer.rs b/crates/bevy_render/src/render_resource/pipeline_specializer.rs index a5cb472aa48f8..d187e199ccd29 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -1,5 +1,12 @@ -use crate::render_resource::{CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor}; -use bevy_utils::HashMap; +use crate::{ + mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout}, + render_resource::{ + CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor, VertexBufferLayout, + }, +}; +use bevy_utils::{ + hashbrown::hash_map::RawEntryMut, Entry, HashMap, Hashed, PreHashMap, PreHashMapExt, +}; use std::hash::Hash; pub struct SpecializedPipelines { @@ -32,3 +39,62 @@ pub trait SpecializedPipeline { type Key: Clone + Hash + PartialEq + Eq; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; } + +pub struct SpecializedMeshPipelines { + mesh_layout_cache: PreHashMap>, + vertex_layout_cache: HashMap>, +} + +impl Default for SpecializedMeshPipelines { + fn default() -> Self { + Self { + mesh_layout_cache: Default::default(), + vertex_layout_cache: Default::default(), + } + } +} + +impl SpecializedMeshPipelines { + #[inline] + pub fn specialize( + &mut self, + cache: &mut RenderPipelineCache, + specialize_pipeline: &S, + key: S::Key, + layout: &MeshVertexBufferLayout, + ) -> CachedPipelineId { + let map = self + .mesh_layout_cache + .get_or_insert_with(layout, Default::default); + *map.entry(key.clone()).or_insert_with(|| { + let descriptor = specialize_pipeline.specialize(key.clone(), layout); + // 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 + } + }; + match layout_map.entry(key) { + Entry::Occupied(entry) => *entry.into_mut(), + Entry::Vacant(entry) => *entry.insert(cache.queue(descriptor)), + } + }) + } +} + +pub trait SpecializedMeshPipeline { + type Key: Clone + Hash + PartialEq + Eq; + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor; +} diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 0e3ceaae55cc0..c91bfe676d7d9 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -12,7 +12,7 @@ use bevy_ecs::{ world::FromWorld, }; use bevy_render::{ - mesh::Mesh, + mesh::{Mesh, MeshVertexBufferLayout}, render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets}, render_component::ExtractComponentPlugin, render_phase::{ @@ -21,7 +21,7 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + SpecializedMeshPipeline, SpecializedMeshPipelines, }, renderer::RenderDevice, view::{ComputedVisibility, Msaa, Visibility, VisibleEntities}, @@ -78,7 +78,12 @@ impl SpecializedMaterial2d for M { fn key(_material: &::PreparedAsset) -> Self::Key {} #[inline] - fn specialize(_key: Self::Key, _descriptor: &mut RenderPipelineDescriptor) {} + fn specialize( + _key: Self::Key, + _descriptor: &mut RenderPipelineDescriptor, + _layout: &MeshVertexBufferLayout, + ) { + } #[inline] fn bind_group(material: &::PreparedAsset) -> &BindGroup { @@ -122,7 +127,11 @@ pub trait SpecializedMaterial2d: Asset + RenderAsset { fn key(material: &::PreparedAsset) -> Self::Key; /// Specializes the given `descriptor` according to the given `key`. - fn specialize(key: Self::Key, descriptor: &mut RenderPipelineDescriptor); + fn specialize( + key: Self::Key, + descriptor: &mut RenderPipelineDescriptor, + layout: &MeshVertexBufferLayout, + ); /// Returns this material's [`BindGroup`]. This should match the layout returned by [`SpecializedMaterial2d::bind_group_layout`]. fn bind_group(material: &::PreparedAsset) -> &BindGroup; @@ -172,7 +181,7 @@ impl Plugin for Material2dPlugin { render_app .add_render_command::>() .init_resource::>() - .init_resource::>>() + .init_resource::>>() .add_system_to_stage(RenderStage::Queue, queue_material2d_meshes::); } } @@ -186,11 +195,15 @@ pub struct Material2dPipeline { marker: PhantomData, } -impl SpecializedPipeline for Material2dPipeline { +impl SpecializedMeshPipeline for Material2dPipeline { type Key = (Mesh2dPipelineKey, M::Key); - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh2d_pipeline.specialize(key.0); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut descriptor = self.mesh2d_pipeline.specialize(key.0, layout); if let Some(vertex_shader) = &self.vertex_shader { descriptor.vertex.shader = vertex_shader.clone(); } @@ -204,7 +217,7 @@ impl SpecializedPipeline for Material2dPipeline { self.mesh2d_pipeline.mesh_layout.clone(), ]); - M::specialize(key.1, &mut descriptor); + M::specialize(key.1, &mut descriptor, layout); descriptor } } @@ -259,7 +272,7 @@ impl EntityRenderCommand pub fn queue_material2d_meshes( transparent_draw_functions: Res>, material2d_pipeline: Res>, - mut pipelines: ResMut>>, + mut pipelines: ResMut>>, mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, @@ -276,42 +289,39 @@ pub fn queue_material2d_meshes( .get_id::>() .unwrap(); - let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples); + let msaa_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples); for visible_entity in &visible_entities.entities { if let Ok((material2d_handle, mesh2d_handle, mesh2d_uniform)) = material2d_meshes.get(*visible_entity) { if let Some(material2d) = render_materials.get(material2d_handle) { - let mut mesh2d_key = mesh_key; if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) { - if mesh.has_tangents { - mesh2d_key |= Mesh2dPipelineKey::VERTEX_TANGENTS; - } - mesh2d_key |= - Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); + let mesh2d_key = msaa_key + | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); + + let specialized_key = M::key(material2d); + let pipeline_id = pipelines.specialize( + &mut pipeline_cache, + &material2d_pipeline, + (mesh2d_key, specialized_key), + &mesh.layout, + ); + + let mesh_z = mesh2d_uniform.transform.w_axis.z; + transparent_phase.add(Transparent2d { + entity: *visible_entity, + draw_function: draw_transparent_pbr, + pipeline: pipeline_id, + // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the + // lowest sort key and getting closer should increase. As we have + // -z in front of the camera, the largest distance is -far with values increasing toward the + // camera. As such we can just use mesh_z as the distance + sort_key: FloatOrd(mesh_z), + // This material is not batched + batch_range: None, + }); } - - let specialized_key = M::key(material2d); - let pipeline_id = pipelines.specialize( - &mut pipeline_cache, - &material2d_pipeline, - (mesh2d_key, specialized_key), - ); - - let mesh_z = mesh2d_uniform.transform.w_axis.z; - transparent_phase.add(Transparent2d { - entity: *visible_entity, - draw_function: draw_transparent_pbr, - pipeline: pipeline_id, - // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the - // lowest sort key and getting closer should increase. As we have - // -z in front of the camera, the largest distance is -far with values increasing toward the - // camera. As such we can just use mesh_z as the distance - sort_key: FloatOrd(mesh_z), - // This material is not batched - batch_range: None, - }); } } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 7468990b47ea8..a87cd4f36570c 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -7,7 +7,7 @@ use bevy_ecs::{ use bevy_math::{Mat4, Size}; use bevy_reflect::TypeUuid; use bevy_render::{ - mesh::{GpuBufferInfo, Mesh}, + mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, @@ -64,7 +64,7 @@ impl Plugin for Mesh2dRenderPlugin { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_mesh2d) .add_system_to_stage(RenderStage::Queue, queue_mesh2d_bind_group) .add_system_to_stage(RenderStage::Queue, queue_mesh2d_view_bind_groups); @@ -234,7 +234,6 @@ bitflags::bitflags! { // FIXME: make normals optional? pub struct Mesh2dPipelineKey: u32 { const NONE = 0; - const VERTEX_TANGENTS = (1 << 0); const MSAA_RESERVED_BITS = Mesh2dPipelineKey::MSAA_MASK_BITS << Mesh2dPipelineKey::MSAA_SHIFT_BITS; const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Mesh2dPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << Mesh2dPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS; } @@ -276,84 +275,39 @@ impl Mesh2dPipelineKey { } } -impl SpecializedPipeline for Mesh2dPipeline { +impl SpecializedMeshPipeline for Mesh2dPipeline { type Key = Mesh2dPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let (vertex_array_stride, vertex_attributes) = - if key.contains(Mesh2dPipelineKey::VERTEX_TANGENTS) { - ( - 48, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv (GOTCHA! uv is no longer third in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 40, - shader_location: 2, - }, - // Tangent - VertexAttribute { - format: VertexFormat::Float32x4, - offset: 24, - shader_location: 3, - }, - ], - ) - } else { - ( - 32, - vec![ - // Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically)) - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 12, - shader_location: 0, - }, - // Normal - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }, - // Uv - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 24, - shader_location: 2, - }, - ], - ) - }; + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut vertex_attributes = vec![ + Mesh::ATTRIBUTE_POSITION.at_shader_location(0), + Mesh::ATTRIBUTE_NORMAL.at_shader_location(1), + Mesh::ATTRIBUTE_UV_0.at_shader_location(2), + ]; + let mut shader_defs = Vec::new(); - if key.contains(Mesh2dPipelineKey::VERTEX_TANGENTS) { + if layout.contains(Mesh::ATTRIBUTE_TANGENT) { shader_defs.push(String::from("VERTEX_TANGENTS")); + vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3)); } #[cfg(feature = "webgl")] shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT")); + let vertex_buffer_layout = layout + .get_layout(&vertex_attributes) + .expect("Mesh is missing a vertex attribute"); + RenderPipelineDescriptor { vertex: VertexState { shader: MESH2D_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: shader_defs.clone(), - buffers: vec![VertexBufferLayout { - array_stride: vertex_array_stride, - step_mode: VertexStepMode::Vertex, - attributes: vertex_attributes, - }], + buffers: vec![vertex_buffer_layout], }, fragment: Some(FragmentState { shader: MESH2D_SHADER_HANDLE.typed::(), diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 4eceaf2bdfc87..e9f00bb710332 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -113,31 +113,24 @@ impl SpecializedPipeline for SpritePipeline { type Key = SpritePipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut vertex_buffer_layout = VertexBufferLayout { - array_stride: 20, - step_mode: VertexStepMode::Vertex, - attributes: vec![ - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 0, - }, - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 12, - shader_location: 1, - }, - ], - }; + let mut formats = vec![ + // position + VertexFormat::Float32x3, + // uv + VertexFormat::Float32x2, + ]; + + if key.contains(SpritePipelineKey::COLORED) { + // color + formats.push(VertexFormat::Uint32); + } + + let vertex_layout = + VertexBufferLayout::from_vertex_formats(VertexStepMode::Vertex, formats); + let mut shader_defs = Vec::new(); if key.contains(SpritePipelineKey::COLORED) { shader_defs.push("COLORED".to_string()); - vertex_buffer_layout.attributes.push(VertexAttribute { - format: VertexFormat::Uint32, - offset: 20, - shader_location: 2, - }); - vertex_buffer_layout.array_stride += 4; } RenderPipelineDescriptor { @@ -145,7 +138,7 @@ impl SpecializedPipeline for SpritePipeline { shader: SPRITE_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: shader_defs.clone(), - buffers: vec![vertex_buffer_layout], + buffers: vec![vertex_layout.clone()], }, fragment: Some(FragmentState { shader: SPRITE_SHADER_HANDLE.typed::(), diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index 4538dc922b131..082b88ed4370d 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -66,29 +66,17 @@ impl SpecializedPipeline for UiPipeline { type Key = UiPipelineKey; /// FIXME: there are no specialization for now, should this be removed? fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor { - let vertex_buffer_layout = VertexBufferLayout { - array_stride: 24, - step_mode: VertexStepMode::Vertex, - attributes: vec![ - // Position - VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 0, - }, - // UV - VertexAttribute { - format: VertexFormat::Float32x2, - offset: 12, - shader_location: 1, - }, - VertexAttribute { - format: VertexFormat::Uint32, - offset: 20, - shader_location: 2, - }, + let vertex_layout = VertexBufferLayout::from_vertex_formats( + VertexStepMode::Vertex, + vec![ + // position + VertexFormat::Float32x3, + // uv + VertexFormat::Float32x2, + // color + VertexFormat::Uint32, ], - }; + ); let shader_defs = Vec::new(); RenderPipelineDescriptor { @@ -96,7 +84,7 @@ impl SpecializedPipeline for UiPipeline { shader: super::UI_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: shader_defs.clone(), - buffers: vec![vertex_buffer_layout], + buffers: vec![vertex_layout.clone()], }, fragment: Some(FragmentState { shader: super::UI_SHADER_HANDLE.typed::(), diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 371645839ac08..6fec365bb617f 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -68,11 +68,11 @@ fn star( v_pos.push([r * a.cos(), r * a.sin(), 0.0]); } // Set the position attribute - star.set_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); + star.insert_attribute(Mesh::ATTRIBUTE_POSITION, v_pos); // And a RGB color attribute as well let mut v_color = vec![[0.0, 0.0, 0.0, 1.0]]; v_color.extend_from_slice(&[[1.0, 1.0, 0.0, 1.0]; 10]); - star.set_attribute(Mesh::ATTRIBUTE_COLOR, v_color); + star.insert_attribute(Mesh::ATTRIBUTE_COLOR, v_color); // Now, we specify the indices of the vertex that are going to compose the // triangles in our star. Vertices in triangles have to be specified in CCW diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 6e8f4b00af0fc..0fc09c6c2fbf5 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -7,6 +7,8 @@ use bevy::{ }, prelude::*, render::{ + mesh::MeshVertexBufferLayout, + render_asset::RenderAssets, render_phase::{ AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass, @@ -66,7 +68,7 @@ impl Plugin for CustomMaterialPlugin { bind_group: None, }) .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_time) .add_system_to_stage(RenderStage::Extract, extract_custom_material) .add_system_to_stage(RenderStage::Prepare, prepare_time) @@ -94,9 +96,10 @@ fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, + mut pipelines: ResMut>, mut pipeline_cache: ResMut, - material_meshes: Query<(Entity, &MeshUniform), (With>, With)>, + render_meshes: Res>, + material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { let draw_custom = transparent_3d_draw_functions @@ -106,18 +109,21 @@ fn queue_custom( let key = MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); - let pipeline = pipelines.specialize(&mut pipeline_cache, &custom_pipeline, key); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); - for (entity, mesh_uniform) in material_meshes.iter() { - transparent_phase.add(Transparent3d { - entity, - pipeline, - draw_function: draw_custom, - distance: view_row_2.dot(mesh_uniform.transform.col(3)), - }); + for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { + if let Some(mesh) = render_meshes.get(mesh_handle) { + let pipeline = + pipelines.specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout); + transparent_phase.add(Transparent3d { + entity, + pipeline, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } } } } @@ -207,11 +213,15 @@ impl FromWorld for CustomPipeline { } } -impl SpecializedPipeline for CustomPipeline { +impl SpecializedMeshPipeline for CustomPipeline { type Key = MeshPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh_pipeline.specialize(key); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut descriptor = self.mesh_pipeline.specialize(key, layout); descriptor.vertex.shader = self.shader.clone(); descriptor.fragment.as_mut().unwrap().shader = self.shader.clone(); descriptor.layout = Some(vec![ diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index bec75411f0a3e..e7325d7eef12f 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -6,12 +6,13 @@ use bevy::{ }, prelude::*, render::{ + mesh::MeshVertexBufferLayout, render_asset::RenderAssets, render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ - RenderPipelineCache, RenderPipelineDescriptor, SpecializedPipeline, - SpecializedPipelines, + RenderPipelineCache, RenderPipelineDescriptor, SpecializedMeshPipeline, + SpecializedMeshPipelines, }, view::ExtractedView, RenderApp, RenderStage, @@ -26,7 +27,7 @@ impl Plugin for IsRedPlugin { app.sub_app_mut(RenderApp) .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom); } } @@ -98,15 +99,19 @@ impl FromWorld for IsRedPipeline { } } -impl SpecializedPipeline for IsRedPipeline { +impl SpecializedMeshPipeline for IsRedPipeline { type Key = (IsRed, MeshPipelineKey); - fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor { + fn specialize( + &self, + (is_red, pbr_pipeline_key): Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { let mut shader_defs = Vec::new(); if is_red.0 { shader_defs.push("IS_RED".to_string()); } - let mut descriptor = self.mesh_pipeline.specialize(pbr_pipeline_key); + let mut descriptor = self.mesh_pipeline.specialize(pbr_pipeline_key, layout); descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.shader_defs = shader_defs.clone(); let fragment = descriptor.fragment.as_mut().unwrap(); @@ -133,7 +138,7 @@ fn queue_custom( render_meshes: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, + mut pipelines: ResMut>, mut pipeline_cache: ResMut, material_meshes: Query<(Entity, &Handle, &MeshUniform, &IsRed)>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, @@ -142,15 +147,20 @@ fn queue_custom( .read() .get_id::() .unwrap(); - let key = MeshPipelineKey::from_msaa_samples(msaa.samples); + let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); for (entity, mesh_handle, mesh_uniform, is_red) in material_meshes.iter() { if let Some(mesh) = render_meshes.get(mesh_handle) { - let key = key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - let pipeline = - pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key)); + let key = + msaa_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline = pipelines.specialize( + &mut pipeline_cache, + &custom_pipeline, + (*is_red, key), + &mesh.layout, + ); transparent_phase.add(Transparent3d { entity, pipeline, diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 4c88cd17481e4..c620cb36fc857 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -5,7 +5,7 @@ use bevy::{ pbr::{MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup}, prelude::*, render::{ - mesh::GpuBufferInfo, + mesh::{GpuBufferInfo, MeshVertexBufferLayout}, render_asset::RenderAssets, render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{ @@ -81,7 +81,7 @@ impl Plugin for CustomMaterialPlugin { app.sub_app_mut(RenderApp) .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom) .add_system_to_stage(RenderStage::Prepare, prepare_instance_buffers); } @@ -99,10 +99,11 @@ fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, + mut pipelines: ResMut>, mut pipeline_cache: ResMut, + meshes: Res>, material_meshes: Query< - (Entity, &MeshUniform), + (Entity, &MeshUniform, &Handle), (With>, With), >, mut views: Query<(&ExtractedView, &mut RenderPhase)>, @@ -112,20 +113,24 @@ fn queue_custom( .get_id::() .unwrap(); - let key = MeshPipelineKey::from_msaa_samples(msaa.samples) - | MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); - let pipeline = pipelines.specialize(&mut pipeline_cache, &custom_pipeline, key); + let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); - for (entity, mesh_uniform) in material_meshes.iter() { - transparent_phase.add(Transparent3d { - entity, - pipeline, - draw_function: draw_custom, - distance: view_row_2.dot(mesh_uniform.transform.col(3)), - }); + for (entity, mesh_uniform, mesh_handle) in material_meshes.iter() { + if let Some(mesh) = meshes.get(mesh_handle) { + let key = + msaa_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline = + pipelines.specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout); + transparent_phase.add(Transparent3d { + entity, + pipeline, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } } } } @@ -175,11 +180,15 @@ impl FromWorld for CustomPipeline { } } -impl SpecializedPipeline for CustomPipeline { +impl SpecializedMeshPipeline for CustomPipeline { type Key = MeshPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh_pipeline.specialize(key); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> RenderPipelineDescriptor { + let mut descriptor = self.mesh_pipeline.specialize(key, layout); descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.buffers.push(VertexBufferLayout { array_stride: std::mem::size_of::() as u64, diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index d1bb8b4b20818..984bdc75119b7 100644 --- a/examples/shader/shader_material_glsl.rs +++ b/examples/shader/shader_material_glsl.rs @@ -4,6 +4,7 @@ use bevy::{ prelude::*, reflect::TypeUuid, render::{ + mesh::MeshVertexBufferLayout, render_asset::{PrepareAssetError, RenderAsset}, render_resource::{ std140::{AsStd140, Std140}, @@ -95,7 +96,11 @@ impl SpecializedMaterial for CustomMaterial { fn key(_: &::PreparedAsset) -> Self::Key {} - fn specialize(_: Self::Key, descriptor: &mut RenderPipelineDescriptor) { + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + _: Self::Key, + _layout: &MeshVertexBufferLayout, + ) { descriptor.vertex.entry_point = "main".into(); descriptor.fragment.as_mut().unwrap().entry_point = "main".into(); }