diff --git a/Cargo.toml b/Cargo.toml index ef6cb40a7da7b..ad3e7a94a30e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -444,6 +444,10 @@ name = "scene" path = "examples/scene/scene.rs" # Shaders +[[example]] +name = "custom_vertex_attribute" +path = "examples/shader/custom_vertex_attribute.rs" + [[example]] name = "shader_defs" path = "examples/shader/shader_defs.rs" diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl new file mode 100644 index 0000000000000..3e42b03d710ae --- /dev/null +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -0,0 +1,40 @@ +#import bevy_pbr::mesh_view_bind_group +#import bevy_pbr::mesh_struct + +struct Vertex { + [[location(0)]] position: vec3; + [[location(1)]] blend_color: vec4; +}; + +struct CustomMaterial { + color: vec4; +}; +[[group(1), binding(0)]] +var material: CustomMaterial; + +[[group(2), binding(0)]] +var mesh: Mesh; + +struct VertexOutput { + [[builtin(position)]] clip_position: vec4; + [[location(0)]] blend_color: vec4; +}; + +[[stage(vertex)]] +fn vertex(vertex: Vertex) -> VertexOutput { + let world_position = mesh.model * vec4(vertex.position, 1.0); + + var out: VertexOutput; + out.clip_position = view.view_proj * world_position; + out.blend_color = vertex.blend_color; + return out; +} + +struct FragmentInput { + [[location(0)]] blend_color: vec4; +}; + +[[stage(fragment)]] +fn fragment(input: FragmentInput) -> [[location(0)]] vec4 { + return material.color * input.blend_color; +} diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 1e85d483fd4d1..a9d1b242f1540 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -125,40 +125,40 @@ 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 // .read_colors(0) // .map(|v| VertexAttributeValues::Float32x4(v.into_rgba_f32().collect())) // { - // mesh.set_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute); + // mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, vertex_attribute); // } if let Some(indices) = reader.read_indices() { 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..d01e4f79a557c 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,12 +24,13 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, view::{ExtractedView, Msaa, VisibleEntities}, RenderApp, RenderStage, }; +use bevy_utils::tracing::error; use std::hash::Hash; use std::marker::PhantomData; @@ -72,6 +73,16 @@ pub trait Material: Asset + RenderAsset { fn dynamic_uniform_indices(material: &::PreparedAsset) -> &[u32] { &[] } + + /// Customizes the default [`RenderPipelineDescriptor`]. + #[allow(unused_variables)] + #[inline] + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + layout: &MeshVertexBufferLayout, + ) -> Result<(), SpecializedMeshPipelineError> { + Ok(()) + } } impl SpecializedMaterial for M { @@ -81,7 +92,13 @@ 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, + ) -> Result<(), SpecializedMeshPipelineError> { + ::specialize(descriptor, layout) + } #[inline] fn bind_group(material: &::PreparedAsset) -> &BindGroup { @@ -130,7 +147,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, + ) -> Result<(), SpecializedMeshPipelineError>; /// Returns this material's [`BindGroup`]. This should match the layout returned by [`SpecializedMaterial::bind_group_layout`]. fn bind_group(material: &::PreparedAsset) -> &BindGroup; @@ -188,12 +209,18 @@ 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::); } } } +#[derive(Eq, PartialEq, Clone, Hash)] +pub struct MaterialPipelineKey { + mesh_key: MeshPipelineKey, + material_key: T, +} + pub struct MaterialPipeline { pub mesh_pipeline: MeshPipeline, pub material_layout: BindGroupLayout, @@ -202,11 +229,15 @@ pub struct MaterialPipeline { marker: PhantomData, } -impl SpecializedPipeline for MaterialPipeline { - type Key = (MeshPipelineKey, M::Key); +impl SpecializedMeshPipeline for MaterialPipeline { + type Key = MaterialPipelineKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh_pipeline.specialize(key.0); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result { + let mut descriptor = self.mesh_pipeline.specialize(key.mesh_key, layout)?; if let Some(vertex_shader) = &self.vertex_shader { descriptor.vertex.shader = vertex_shader.clone(); } @@ -220,8 +251,8 @@ impl SpecializedPipeline for MaterialPipeline { self.mesh_pipeline.mesh_layout.clone(), ]); - M::specialize(key.1, &mut descriptor); - descriptor + M::specialize(&mut descriptor, key.material_key, layout)?; + Ok(descriptor) } } @@ -275,7 +306,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 +338,81 @@ 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 material_key = M::key(material); + + let pipeline_id = pipelines.specialize( + &mut pipeline_cache, + &material_pipeline, + MaterialPipelineKey { + mesh_key, + material_key, + }, + &mesh.layout, + ); + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + continue; + } + }; + + // 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..eaf45397a78a4 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, + ) -> Result<(), SpecializedMeshPipelineError> { if key.normal_map { descriptor .fragment @@ -350,6 +355,7 @@ impl SpecializedMaterial for StandardMaterial { if let Some(label) = &mut descriptor.label { *label = format!("pbr_{}", *label).into(); } + Ok(()) } fn fragment_shader(_asset_server: &AssetServer) -> Option> { diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index cc606057f2944..562c9b6d2031c 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::{ @@ -28,7 +28,10 @@ use bevy_render::{ view::{ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, }; use bevy_transform::components::GlobalTransform; -use bevy_utils::{tracing::warn, HashMap}; +use bevy_utils::{ + tracing::{error, warn}, + HashMap, +}; use std::num::NonZeroU32; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] @@ -214,7 +217,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 +246,23 @@ 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, - }, - ], - ) - }; - RenderPipelineDescriptor { + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result { + let vertex_buffer_layout = + layout.get_layout(&[Mesh::ATTRIBUTE_POSITION.at_shader_location(0)])?; + + Ok(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], }, fragment: None, layout: Some(vec![self.view_layout.clone(), self.mesh_layout.clone()]), @@ -344,7 +293,7 @@ impl SpecializedPipeline for ShadowPipeline { }), multisample: MultisampleState::default(), label: Some("shadow_pipeline".into()), - } + }) } } @@ -1090,7 +1039,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 +1069,32 @@ 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, + ); + + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + continue; + } + }; + + 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..a410e57ed5aa5 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,28 @@ 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, + ) -> Result { + 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)?; + let (label, blend, depth_write_enabled); if key.contains(MeshPipelineKey::TRANSPARENT_MAIN_PASS) { label = "transparent_mesh_pipeline".into(); @@ -495,16 +451,12 @@ impl SpecializedPipeline for MeshPipeline { #[cfg(feature = "webgl")] shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT")); - RenderPipelineDescriptor { + Ok(RenderPipelineDescriptor { vertex: VertexState { 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], }, fragment: Some(FragmentState { shader: MESH_SHADER_HANDLE.typed::(), @@ -548,7 +500,7 @@ impl SpecializedPipeline for MeshPipeline { alpha_to_coverage_enabled: false, }, label: Some(label), - } + }) } } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 9f1308179e518..07e43a2d3cf99 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -5,15 +5,20 @@ 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, RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError, + 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, }; +use bevy_utils::tracing::error; pub const WIREFRAME_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 192598014480025766); @@ -35,7 +40,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,27 +84,32 @@ 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, + ) -> Result { + 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; descriptor.depth_stencil.as_mut().unwrap().bias.slope_scale = 1.0; - descriptor + Ok(descriptor) } } #[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 +121,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,15 +129,24 @@ 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); + let pipeline_id = specialized_pipelines.specialize( + &mut pipeline_cache, + &wireframe_pipeline, + key, + &mesh.layout, + ); + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + return; + } + }; transparent_phase.add(Opaque3d { entity, - pipeline: specialized_pipelines.specialize( - &mut pipeline_cache, - &wireframe_pipeline, - key, - ), + pipeline: pipeline_id, 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..9c7488bdbff5b 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -3,17 +3,19 @@ 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 thiserror::Error; 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 +27,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, } @@ -44,29 +46,39 @@ pub struct Mesh { /// # use bevy_render::render_resource::PrimitiveTopology; /// fn create_triangle() -> Mesh { /// let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); -/// mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]); +/// mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]); /// mesh.set_indices(Some(Indices::U32(vec![0,1,2]))); /// 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::insert_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"; + /// Use in conjunction with [`Mesh::insert_attribute`] + pub const ATTRIBUTE_NORMAL: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Normal", 1, VertexFormat::Float32x3); + + /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::insert_attribute`] + 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: &'static str = "Vertex_Tangent"; + pub const ATTRIBUTE_TANGENT: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_Tangent", 3, VertexFormat::Float32x4); - /// 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"; + /// Per vertex coloring. Use in conjunction with [`Mesh::insert_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"; - /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::set_attribute`] - pub const ATTRIBUTE_JOINT_INDEX: &'static str = "Vertex_JointIndex"; + /// Per vertex joint transform matrix weight. Use in conjunction with [`Mesh::insert_attribute`] + pub const ATTRIBUTE_JOINT_WEIGHT: MeshVertexAttribute = + MeshVertexAttribute::new("Vertex_JointWeight", 5, VertexFormat::Float32x4); + /// Per vertex joint transform matrix index. Use in conjunction with [`Mesh::insert_attribute`] + 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 +98,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 +167,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 +200,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 +220,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 +229,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 +269,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 +322,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 +351,131 @@ 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, self.name) + } +} + +#[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()) + } + + #[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], + ) -> Result { + 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 Err(MissingVertexAttributeError { + id: attribute_descriptor.id, + name: attribute_descriptor.name, + pipeline_type: None, + }); + } + } + + Ok(VertexBufferLayout { + array_stride: self.layout.array_stride, + step_mode: self.layout.step_mode, + attributes, + }) + } +} + +#[derive(Error, Debug)] +#[error("Mesh is missing requested attribute: {name} ({id:?}, pipeline type: {pipeline_type:?})")] +pub struct MissingVertexAttributeError { + pub(crate) pipeline_type: Option<&'static str>, + id: MeshVertexAttributeId, + name: &'static str, +} + +pub struct VertexAttributeDescriptor { + pub shader_location: u32, + pub id: MeshVertexAttributeId, + name: &'static str, +} + +impl VertexAttributeDescriptor { + pub const fn new(shader_location: u32, id: MeshVertexAttributeId, name: &'static str) -> Self { + Self { + shader_location, + id, + name, + } + } +} + +#[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 +683,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 +751,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 +806,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/bind_group_layout.rs b/crates/bevy_render/src/render_resource/bind_group_layout.rs index d17d639c3a77f..de0ce253f8a5f 100644 --- a/crates/bevy_render/src/render_resource/bind_group_layout.rs +++ b/crates/bevy_render/src/render_resource/bind_group_layout.rs @@ -10,6 +10,12 @@ pub struct BindGroupLayout { value: Arc, } +impl PartialEq for BindGroupLayout { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + impl BindGroupLayout { #[inline] pub fn id(&self) -> BindGroupLayoutId { diff --git a/crates/bevy_render/src/render_resource/pipeline.rs b/crates/bevy_render/src/render_resource/pipeline.rs index d3bd820053fdc..caa2e41eb7b3c 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. @@ -87,7 +87,7 @@ impl Deref for ComputePipeline { } /// Describes a render (graphics) pipeline. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct RenderPipelineDescriptor { /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification. pub label: Option>, @@ -105,7 +105,7 @@ pub struct RenderPipelineDescriptor { pub fragment: Option, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct VertexState { /// The compiled shader module for this stage. pub shader: Handle, @@ -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,8 +128,36 @@ 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`](VertexAttribute). + /// 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`](VertexFormat) (in bytes). + pub fn from_vertex_formats>( + step_mode: VertexStepMode, + vertex_formats: T, + ) -> Self { + let mut offset = 0; + let mut attributes = Vec::new(); + for (shader_location, format) in vertex_formats.into_iter().enumerate() { + attributes.push(VertexAttribute { + format, + offset, + shader_location: shader_location as u32, + }); + offset += format.size(); + } + + VertexBufferLayout { + array_stride: offset, + step_mode, + attributes, + } + } +} + /// Describes the fragment process in a render pipeline. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct FragmentState { /// The compiled shader module for this stage. pub shader: Handle, diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 77ed782eb61cb..825a69afb0255 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; @@ -244,6 +244,11 @@ impl RenderPipelineCache { &self.pipelines[id.0].state } + #[inline] + pub fn get_descriptor(&self, id: CachedPipelineId) -> &RenderPipelineDescriptor { + &self.pipelines[id.0].descriptor + } + #[inline] pub fn get(&self, id: CachedPipelineId) -> Option<&RenderPipeline> { if let CachedPipelineState::Ok(pipeline) = &self.pipelines[id.0].state { @@ -345,7 +350,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..517c9333dae30 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -1,6 +1,20 @@ -use crate::render_resource::{CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor}; -use bevy_utils::HashMap; +use crate::{ + mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout, MissingVertexAttributeError}, + render_resource::{ + CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor, VertexBufferLayout, + }, +}; +use bevy_utils::{ + hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap, PreHashMapExt, +}; +use std::fmt::Debug; use std::hash::Hash; +use thiserror::Error; + +pub trait SpecializedPipeline { + type Key: Clone + Hash + PartialEq + Eq; + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; +} pub struct SpecializedPipelines { cache: HashMap, @@ -28,7 +42,89 @@ impl SpecializedPipelines { } } -pub trait SpecializedPipeline { +#[derive(Error, Debug)] +pub enum SpecializedMeshPipelineError { + #[error(transparent)] + MissingVertexAttribute(#[from] MissingVertexAttributeError), +} + +pub trait SpecializedMeshPipeline { type Key: Clone + Hash + PartialEq + Eq; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result; +} + +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, + ) -> Result { + let map = self + .mesh_layout_cache + .get_or_insert_with(layout, Default::default); + match map.entry(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 + } + }; + Ok(*entry.insert(match layout_map.entry(key) { + Entry::Occupied(entry) => { + if cfg!(debug_assertions) { + let stored_descriptor = cache.get_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(descriptor)) + } + })) + } + } + } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 0e3ceaae55cc0..630af30155cb0 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -11,8 +11,9 @@ use bevy_ecs::{ }, world::FromWorld, }; +use bevy_log::error; use bevy_render::{ - mesh::Mesh, + mesh::{Mesh, MeshVertexBufferLayout}, render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets}, render_component::ExtractComponentPlugin, render_phase::{ @@ -21,7 +22,7 @@ use bevy_render::{ }, render_resource::{ BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, view::{ComputedVisibility, Msaa, Visibility, VisibleEntities}, @@ -69,6 +70,16 @@ pub trait Material2d: Asset + RenderAsset { fn dynamic_uniform_indices(material: &::PreparedAsset) -> &[u32] { &[] } + + /// Customizes the default [`RenderPipelineDescriptor`]. + #[allow(unused_variables)] + #[inline] + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + layout: &MeshVertexBufferLayout, + ) -> Result<(), SpecializedMeshPipelineError> { + Ok(()) + } } impl SpecializedMaterial2d for M { @@ -78,7 +89,13 @@ 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, + ) -> Result<(), SpecializedMeshPipelineError> { + ::specialize(descriptor, layout) + } #[inline] fn bind_group(material: &::PreparedAsset) -> &BindGroup { @@ -122,7 +139,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, + ) -> Result<(), SpecializedMeshPipelineError>; /// Returns this material's [`BindGroup`]. This should match the layout returned by [`SpecializedMaterial2d::bind_group_layout`]. fn bind_group(material: &::PreparedAsset) -> &BindGroup; @@ -172,7 +193,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 +207,21 @@ pub struct Material2dPipeline { marker: PhantomData, } -impl SpecializedPipeline for Material2dPipeline { - type Key = (Mesh2dPipelineKey, M::Key); +#[derive(Eq, PartialEq, Clone, Hash)] +pub struct Material2dKey { + mesh_key: Mesh2dPipelineKey, + material_key: T, +} + +impl SpecializedMeshPipeline for Material2dPipeline { + type Key = Material2dKey; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.mesh2d_pipeline.specialize(key.0); + fn specialize( + &self, + key: Self::Key, + layout: &MeshVertexBufferLayout, + ) -> Result { + let mut descriptor = self.mesh2d_pipeline.specialize(key.mesh_key, layout)?; if let Some(vertex_shader) = &self.vertex_shader { descriptor.vertex.shader = vertex_shader.clone(); } @@ -204,8 +235,8 @@ impl SpecializedPipeline for Material2dPipeline { self.mesh2d_pipeline.mesh_layout.clone(), ]); - M::specialize(key.1, &mut descriptor); - descriptor + M::specialize(key.material_key, &mut descriptor, layout)?; + Ok(descriptor) } } @@ -259,7 +290,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 +307,50 @@ 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 mesh_key = msaa_key + | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); + + let material_key = M::key(material2d); + let pipeline_id = pipelines.specialize( + &mut pipeline_cache, + &material2d_pipeline, + Material2dKey { + mesh_key, + material_key, + }, + &mesh.layout, + ); + + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + continue; + } + }; + + 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..2d2a849917fcd 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,37 @@ 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, + ) -> Result { + 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")); - RenderPipelineDescriptor { + let vertex_buffer_layout = layout.get_layout(&vertex_attributes)?; + + Ok(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::(), @@ -382,7 +334,7 @@ impl SpecializedPipeline for Mesh2dPipeline { alpha_to_coverage_enabled: false, }, label: Some("transparent_mesh2d_pipeline".into()), - } + }) } } diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 4eceaf2bdfc87..1243ded649a55 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], }, 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..dee26dfbc729a 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], }, 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/README.md b/examples/README.md index aeaff8c7845f2..d147b0a79c773 100644 --- a/examples/README.md +++ b/examples/README.md @@ -222,6 +222,7 @@ Example | File | Description Example | File | Description --- | --- | --- +`custom_vertex_attribute` | [`shader/custom_vertex_attribute.rs`](./shader/custom_vertex_attribute.rs) | Illustrates creating a custom shader material that reads a mesh's custom vertex attribute. `shader_material` | [`shader/shader_material.rs`](./shader/shader_material.rs) | Illustrates creating a custom material and a shader that uses it `shader_material_glsl` | [`shader/shader_material_glsl.rs`](./shader/shader_material_glsl.rs) | A custom shader using the GLSL shading language. `shader_instancing` | [`shader/shader_instancing.rs`](./shader/shader_instancing.rs) | A custom shader showing off rendering a mesh multiple times in one draw call. diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 6e8f4b00af0fc..b4266bcc8f00d 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) @@ -90,13 +92,15 @@ fn extract_custom_material( } // add each entity with a mesh and a `CustomMaterial` to every view's `Transparent3d` render phase using the `CustomPipeline` +#[allow(clippy::too_many_arguments)] 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 +110,22 @@ 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) + .unwrap(); + transparent_phase.add(Transparent3d { + entity, + pipeline, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } } } } @@ -207,11 +215,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, + ) -> Result { + 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![ @@ -219,7 +231,7 @@ impl SpecializedPipeline for CustomPipeline { self.mesh_pipeline.mesh_layout.clone(), self.time_bind_group_layout.clone(), ]); - descriptor + Ok(descriptor) } } diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs new file mode 100644 index 0000000000000..deb56d3c524d3 --- /dev/null +++ b/examples/shader/custom_vertex_attribute.rs @@ -0,0 +1,150 @@ +use bevy::{ + ecs::system::{lifetimeless::SRes, SystemParamItem}, + pbr::MaterialPipeline, + prelude::*, + reflect::TypeUuid, + render::{ + mesh::{MeshVertexAttribute, MeshVertexBufferLayout}, + render_asset::{PrepareAssetError, RenderAsset}, + render_resource::{ + std140::{AsStd140, Std140}, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, + BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, Buffer, + BufferBindingType, BufferInitDescriptor, BufferSize, BufferUsages, + RenderPipelineDescriptor, ShaderStages, SpecializedMeshPipelineError, VertexFormat, + }, + renderer::RenderDevice, + }, +}; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugin(MaterialPlugin::::default()) + .add_startup_system(setup) + .run(); +} + +// A "high" random id should be used for custom attributes to ensure consistent sorting and avoid collisions with other attributes. +// See the MeshVertexAttribute docs for more info. +const ATTRIBUTE_BLEND_COLOR: MeshVertexAttribute = + MeshVertexAttribute::new("BlendColor", 988540917, VertexFormat::Float32x4); + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + let mut mesh = Mesh::from(shape::Cube { size: 1.0 }); + mesh.insert_attribute( + ATTRIBUTE_BLEND_COLOR, + // The cube mesh has 24 vertices (6 faces, 4 vertices per face), so we insert one BlendColor for each + vec![[1.0, 0.0, 0.0, 1.0]; 24], + ); + + // cube + commands.spawn().insert_bundle(MaterialMeshBundle { + mesh: meshes.add(mesh), + transform: Transform::from_xyz(0.0, 0.5, 0.0), + material: materials.add(CustomMaterial { + color: Color::WHITE, + }), + ..Default::default() + }); + + // camera + commands.spawn_bundle(PerspectiveCameraBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..Default::default() + }); +} + +// This is the struct that will be passed to your shader +#[derive(Debug, Clone, TypeUuid)] +#[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] +pub struct CustomMaterial { + color: Color, +} + +#[derive(Clone)] +pub struct GpuCustomMaterial { + _buffer: Buffer, + bind_group: BindGroup, +} + +// The implementation of [`Material`] needs this impl to work properly. +impl RenderAsset for CustomMaterial { + type ExtractedAsset = CustomMaterial; + type PreparedAsset = GpuCustomMaterial; + type Param = (SRes, SRes>); + fn extract_asset(&self) -> Self::ExtractedAsset { + self.clone() + } + + fn prepare_asset( + extracted_asset: Self::ExtractedAsset, + (render_device, material_pipeline): &mut SystemParamItem, + ) -> Result> { + let color = Vec4::from_slice(&extracted_asset.color.as_linear_rgba_f32()); + let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + contents: color.as_std140().as_bytes(), + label: None, + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + }); + let bind_group = render_device.create_bind_group(&BindGroupDescriptor { + entries: &[BindGroupEntry { + binding: 0, + resource: buffer.as_entire_binding(), + }], + label: None, + layout: &material_pipeline.material_layout, + }); + + Ok(GpuCustomMaterial { + _buffer: buffer, + bind_group, + }) + } +} + +impl Material for CustomMaterial { + fn vertex_shader(asset_server: &AssetServer) -> Option> { + Some(asset_server.load("shaders/custom_vertex_attribute.wgsl")) + } + fn fragment_shader(asset_server: &AssetServer) -> Option> { + Some(asset_server.load("shaders/custom_vertex_attribute.wgsl")) + } + + fn bind_group(render_asset: &::PreparedAsset) -> &BindGroup { + &render_asset.bind_group + } + + fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout { + render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Buffer { + ty: BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: BufferSize::new(Vec4::std140_size_static() as u64), + }, + count: None, + }], + label: None, + }) + } + + fn specialize( + descriptor: &mut RenderPipelineDescriptor, + layout: &MeshVertexBufferLayout, + ) -> Result<(), SpecializedMeshPipelineError> { + let vertex_layout = layout.get_layout(&[ + Mesh::ATTRIBUTE_POSITION.at_shader_location(0), + ATTRIBUTE_BLEND_COLOR.at_shader_location(1), + ])?; + descriptor.vertex.buffers = vec![vertex_layout]; + Ok(()) + } +} diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index bec75411f0a3e..187ec8e51d49d 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, + SpecializedMeshPipelineError, 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, + ) -> Result { 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(); @@ -116,7 +121,7 @@ impl SpecializedPipeline for IsRedPipeline { self.mesh_pipeline.view_layout.clone(), self.mesh_pipeline.mesh_layout.clone(), ]); - descriptor + Ok(descriptor) } } @@ -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,22 @@ 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, + ) + .unwrap(); transparent_phase.add(Transparent3d { entity, pipeline, diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 4c88cd17481e4..f0544c0234c5c 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); } @@ -95,14 +95,16 @@ struct InstanceData { color: [f32; 4], } +#[allow(clippy::too_many_arguments)] 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 +114,25 @@ 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) + .unwrap(); + transparent_phase.add(Transparent3d { + entity, + pipeline, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } } } } @@ -175,11 +182,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, + ) -> Result { + 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, @@ -203,7 +214,7 @@ impl SpecializedPipeline for CustomPipeline { self.mesh_pipeline.mesh_layout.clone(), ]); - descriptor + Ok(descriptor) } } diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index d1bb8b4b20818..2f9dab493e0f7 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,9 +96,14 @@ 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, + ) -> Result<(), SpecializedMeshPipelineError> { descriptor.vertex.entry_point = "main".into(); descriptor.fragment.as_mut().unwrap().entry_point = "main".into(); + Ok(()) } fn vertex_shader(asset_server: &AssetServer) -> Option> {