From 8e15297148b45bc3d40422767eae70ac083db248 Mon Sep 17 00:00:00 2001 From: Jay Oster Date: Fri, 12 Nov 2021 21:20:13 -0800 Subject: [PATCH] Add vertex buffer attribute descriptors to resolve sorting issues - Vertex attribute buffer descriptors can be set as a resource (typed with `Mesh` and `SpriteBatch`, for 3D and 2D renderers respectively) to override the vertex attribute buffer layout at runtime. Defaults to sane layouts provided by `Mesh` and `SpriteBatch`. - `SpecializedPipeline` has a new associated type for the vertex attribute buffer descriptor accepted by the `specialize` method. - Discussion: https://discord.com/channels/691052431525675048/743663924229963868/908484759833960489 Fix vertex buffer layout overrides for `Sprite` --- examples/shader/custom_shader_pipelined.rs | 12 +- examples/shader/shader_defs_pipelined.rs | 17 +- pipelined/bevy_pbr2/src/render/light.rs | 87 +++------ pipelined/bevy_pbr2/src/render/mod.rs | 86 +++----- pipelined/bevy_render2/src/mesh/mesh/mod.rs | 101 ++++++---- .../bevy_render2/src/render_resource/mod.rs | 2 + .../render_resource/pipeline_specializer.rs | 14 +- .../src/render_resource/vertex_attributes.rs | 183 ++++++++++++++++++ pipelined/bevy_sprite2/src/render/mod.rs | 68 ++++--- 9 files changed, 368 insertions(+), 202 deletions(-) create mode 100644 pipelined/bevy_render2/src/render_resource/vertex_attributes.rs diff --git a/examples/shader/custom_shader_pipelined.rs b/examples/shader/custom_shader_pipelined.rs index 9af6b39dcd00e1..daadb5b12cede0 100644 --- a/examples/shader/custom_shader_pipelined.rs +++ b/examples/shader/custom_shader_pipelined.rs @@ -134,9 +134,14 @@ pub struct CustomPipeline { impl SpecializedPipeline for CustomPipeline { type Key = PbrPipelineKey; - - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let mut descriptor = self.pbr_pipeline.specialize(key); + type VertexSpecialization = Mesh; + + fn specialize( + &self, + key: Self::Key, + vbd: &VertexBufferDescriptor, + ) -> RenderPipelineDescriptor { + let mut descriptor = self.pbr_pipeline.specialize(key, vbd); descriptor.vertex.shader = self.shader.clone(); descriptor.fragment.as_mut().unwrap().shader = self.shader.clone(); descriptor.layout = Some(vec![ @@ -201,6 +206,7 @@ pub fn queue_custom( &mut pipeline_cache, &custom_pipeline, key, + &Mesh::get_default_vertex_buffer_descriptor(), ), draw_function: draw_custom, distance: view_row_2.dot(mesh_uniform.transform.col(3)), diff --git a/examples/shader/shader_defs_pipelined.rs b/examples/shader/shader_defs_pipelined.rs index c34604007abc67..5fceb576fea187 100644 --- a/examples/shader/shader_defs_pipelined.rs +++ b/examples/shader/shader_defs_pipelined.rs @@ -104,13 +104,18 @@ impl FromWorld for IsRedPipeline { impl SpecializedPipeline for IsRedPipeline { type Key = (IsRed, PbrPipelineKey); + type VertexSpecialization = Mesh; - fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor { + fn specialize( + &self, + (is_red, pbr_pipeline_key): Self::Key, + vbd: &VertexBufferDescriptor, + ) -> RenderPipelineDescriptor { let mut shader_defs = Vec::new(); if is_red.0 { shader_defs.push("IS_RED".to_string()); } - let mut descriptor = self.pbr_pipeline.specialize(pbr_pipeline_key); + let mut descriptor = self.pbr_pipeline.specialize(pbr_pipeline_key, vbd); descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.shader_defs = shader_defs.clone(); let fragment = descriptor.fragment.as_mut().unwrap(); @@ -149,8 +154,12 @@ fn queue_custom( let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); for (entity, mesh_uniform, is_red) in material_meshes.iter() { - let pipeline = - pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key)); + let pipeline = pipelines.specialize( + &mut pipeline_cache, + &custom_pipeline, + (*is_red, key), + &Mesh::get_default_vertex_buffer_descriptor(), + ); transparent_phase.add(Transparent3d { entity, pipeline, diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index 90c474c8d59d4e..dba898a68a7f39 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -179,73 +179,22 @@ bitflags::bitflags! { impl SpecializedPipeline for ShadowPipeline { type Key = ShadowPipelineKey; + type VertexSpecialization = Mesh; - 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, + vbd: &VertexBufferDescriptor, + ) -> RenderPipelineDescriptor { RenderPipelineDescriptor { vertex: VertexState { shader: SHADOW_SHADER_HANDLE.typed::(), entry_point: "vertex".into(), shader_defs: vec![], buffers: vec![VertexBufferLayout { - array_stride: vertex_array_stride, + array_stride: vbd.stride(), step_mode: VertexStepMode::Vertex, - attributes: vertex_attributes, + attributes: vbd.to_raw(), }], }, fragment: None, @@ -755,13 +704,25 @@ pub fn queue_shadows( for VisibleEntity { entity, .. } in visible_entities.iter() { let mut key = ShadowPipelineKey::empty(); if let Ok(mesh_handle) = casting_meshes.get(*entity) { - if let Some(mesh) = render_meshes.get(mesh_handle) { + let pipeline_id = if let Some(mesh) = render_meshes.get(mesh_handle) { if mesh.has_tangents { key |= ShadowPipelineKey::VERTEX_TANGENTS; } - } - let pipeline_id = - pipelines.specialize(&mut pipeline_cache, &shadow_pipeline, key); + + pipelines.specialize( + &mut pipeline_cache, + &shadow_pipeline, + key, + &mesh.vertex_buffer_descriptor, + ) + } else { + pipelines.specialize( + &mut pipeline_cache, + &shadow_pipeline, + key, + &Mesh::get_default_vertex_buffer_descriptor(), + ) + }; shadow_phase.add(Shadow { draw_function: draw_shadow_mesh, diff --git a/pipelined/bevy_pbr2/src/render/mod.rs b/pipelined/bevy_pbr2/src/render/mod.rs index c1d08028c2439d..68e38361313646 100644 --- a/pipelined/bevy_pbr2/src/render/mod.rs +++ b/pipelined/bevy_pbr2/src/render/mod.rs @@ -446,64 +446,13 @@ impl PbrPipelineKey { impl SpecializedPipeline for PbrPipeline { type Key = PbrPipelineKey; + type VertexSpecialization = Mesh; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { - let (vertex_array_stride, vertex_attributes) = - if key.contains(PbrPipelineKey::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, + vbd: &VertexBufferDescriptor, + ) -> RenderPipelineDescriptor { let mut shader_defs = Vec::new(); if key.contains(PbrPipelineKey::VERTEX_TANGENTS) { shader_defs.push(String::from("VERTEX_TANGENTS")); @@ -517,9 +466,9 @@ impl SpecializedPipeline for PbrPipeline { entry_point: "vertex".into(), shader_defs: shader_defs.clone(), buffers: vec![VertexBufferLayout { - array_stride: vertex_array_stride, + array_stride: vbd.stride(), step_mode: VertexStepMode::Vertex, - attributes: vertex_attributes, + attributes: vbd.to_raw(), }], }, fragment: Some(FragmentState { @@ -704,12 +653,25 @@ pub fn queue_meshes( } else { continue; } - if let Some(mesh) = render_meshes.get(mesh_handle) { + let pipeline_id = if let Some(mesh) = render_meshes.get(mesh_handle) { if mesh.has_tangents { key |= PbrPipelineKey::VERTEX_TANGENTS; } - } - let pipeline_id = pipelines.specialize(&mut pipeline_cache, &pbr_pipeline, key); + + pipelines.specialize( + &mut pipeline_cache, + &pbr_pipeline, + key, + &mesh.vertex_buffer_descriptor, + ) + } else { + pipelines.specialize( + &mut pipeline_cache, + &pbr_pipeline, + key, + &Mesh::get_default_vertex_buffer_descriptor(), + ) + }; // NOTE: row 2 of the view matrix dotted with column 3 of the model matrix // gives the z component of translation of the mesh in view space diff --git a/pipelined/bevy_render2/src/mesh/mesh/mod.rs b/pipelined/bevy_render2/src/mesh/mesh/mod.rs index 204af9445d2e7c..8830d52c048c4a 100644 --- a/pipelined/bevy_render2/src/mesh/mesh/mod.rs +++ b/pipelined/bevy_render2/src/mesh/mesh/mod.rs @@ -3,7 +3,7 @@ mod conversions; use crate::{ primitives::Aabb, render_asset::{PrepareAssetError, RenderAsset}, - render_resource::Buffer, + render_resource::{Buffer, VertexBufferDescriptor}, renderer::RenderDevice, }; use bevy_core::cast_slice; @@ -128,28 +128,6 @@ 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, - // } - // } - pub fn count_vertices(&self) -> usize { let mut vertex_count: Option = None; for (attribute_name, attribute_data) in self.attributes.iter() { @@ -164,19 +142,27 @@ impl Mesh { vertex_count.unwrap_or(0) } - 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); - vertex_size += vertex_format.get_size() as usize; - } - + pub fn get_vertex_buffer_data(&self, vbd: &VertexBufferDescriptor) -> Vec { let vertex_count = self.count_vertices(); + let vertex_size = vbd.stride() as usize; + 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() { + for (attribute_name, attribute_values) in self.attributes.iter() { + let vertex_layout = vbd.get_attribute_layout(attribute_name).unwrap_or_else(|| { + panic!( + "VertexBufferDescriptor is missing an attribute named {}", + attribute_name, + ); + }); + let attribute_offset = vertex_layout.offset as usize; + let vertex_format = VertexFormat::from(attribute_values); + assert_eq!( + vertex_format, vertex_layout.format, + "Attribute {} has a different format than the VertexBufferDescriptor: {:?} != {:?}", + attribute_name, vertex_format, vertex_layout.format + ); let attribute_size = vertex_format.get_size() as usize; let attributes_bytes = attribute_values.get_bytes(); for (vertex_index, attribute_bytes) in @@ -186,13 +172,40 @@ impl Mesh { attributes_interleaved_buffer[offset..offset + attribute_size] .copy_from_slice(attribute_bytes); } - - attribute_offset += attribute_size; } attributes_interleaved_buffer } + fn get_specialized_vertex_buffer_descriptor(&self) -> VertexBufferDescriptor { + let mut vbd = VertexBufferDescriptor::default(); + + vbd.push(Self::ATTRIBUTE_POSITION, VertexFormat::Float32x3, 0); + vbd.push(Self::ATTRIBUTE_NORMAL, VertexFormat::Float32x3, 1); + vbd.push(Self::ATTRIBUTE_UV_0, VertexFormat::Float32x2, 2); + + if self.attributes.contains_key(Self::ATTRIBUTE_TANGENT) { + vbd.push(Self::ATTRIBUTE_TANGENT, VertexFormat::Float32x4, 3); + } + + if self.attributes.contains_key(Self::ATTRIBUTE_JOINT_INDEX) { + vbd.push(Self::ATTRIBUTE_JOINT_WEIGHT, VertexFormat::Float32x4, 4); + vbd.push(Self::ATTRIBUTE_JOINT_INDEX, VertexFormat::Uint32x4, 5); + } + + vbd + } + + pub fn get_default_vertex_buffer_descriptor() -> VertexBufferDescriptor { + let mut vbd = VertexBufferDescriptor::default(); + + vbd.push(Self::ATTRIBUTE_POSITION, VertexFormat::Float32x3, 0); + vbd.push(Self::ATTRIBUTE_NORMAL, VertexFormat::Float32x3, 1); + vbd.push(Self::ATTRIBUTE_UV_0, VertexFormat::Float32x2, 2); + + vbd + } + /// Duplicates the vertex attributes so that no vertices are shared. /// /// This can dramatically increase the vertex count, so make sure this is what you want. @@ -560,6 +573,7 @@ impl From<&Indices> for IndexFormat { #[derive(Debug, Clone)] pub struct GpuMesh { pub vertex_buffer: Buffer, + pub vertex_buffer_descriptor: VertexBufferDescriptor, pub index_info: Option, pub has_tangents: bool, } @@ -574,7 +588,10 @@ pub struct GpuIndexInfo { impl RenderAsset for Mesh { type ExtractedAsset = Mesh; type PreparedAsset = GpuMesh; - type Param = SRes; + type Param = ( + SRes, + Option>>, + ); fn extract_asset(&self) -> Self::ExtractedAsset { self.clone() @@ -582,17 +599,22 @@ impl RenderAsset for Mesh { fn prepare_asset( mesh: Self::ExtractedAsset, - render_device: &mut SystemParamItem, + params: &mut SystemParamItem, ) -> Result> { - let vertex_buffer_data = mesh.get_vertex_buffer_data(); - let vertex_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { + let vbd = match params.1.as_deref() { + Some(vbd) => vbd.clone(), + None => mesh.get_specialized_vertex_buffer_descriptor(), + }; + + let vertex_buffer_data = mesh.get_vertex_buffer_data(&vbd); + let vertex_buffer = params.0.create_buffer_with_data(&BufferInitDescriptor { usage: BufferUsages::VERTEX, label: None, contents: &vertex_buffer_data, }); let index_info = mesh.get_index_buffer_bytes().map(|data| GpuIndexInfo { - buffer: render_device.create_buffer_with_data(&BufferInitDescriptor { + buffer: params.0.create_buffer_with_data(&BufferInitDescriptor { usage: BufferUsages::INDEX, contents: data, label: None, @@ -603,6 +625,7 @@ impl RenderAsset for Mesh { Ok(GpuMesh { vertex_buffer, + vertex_buffer_descriptor: vbd, index_info, has_tangents: mesh.attributes.contains_key(Mesh::ATTRIBUTE_TANGENT), }) diff --git a/pipelined/bevy_render2/src/render_resource/mod.rs b/pipelined/bevy_render2/src/render_resource/mod.rs index 6b2c6ee7101b27..eafeb9fad7662a 100644 --- a/pipelined/bevy_render2/src/render_resource/mod.rs +++ b/pipelined/bevy_render2/src/render_resource/mod.rs @@ -8,6 +8,7 @@ mod pipeline_specializer; mod shader; mod texture; mod uniform_vec; +mod vertex_attributes; pub use bind_group::*; pub use bind_group_layout::*; @@ -19,6 +20,7 @@ pub use pipeline_specializer::*; pub use shader::*; pub use texture::*; pub use uniform_vec::*; +pub use vertex_attributes::*; // TODO: decide where re-exports should go pub use wgpu::{ diff --git a/pipelined/bevy_render2/src/render_resource/pipeline_specializer.rs b/pipelined/bevy_render2/src/render_resource/pipeline_specializer.rs index a5cb472aa48f8b..0a61d9f418a7f1 100644 --- a/pipelined/bevy_render2/src/render_resource/pipeline_specializer.rs +++ b/pipelined/bevy_render2/src/render_resource/pipeline_specializer.rs @@ -1,4 +1,6 @@ -use crate::render_resource::{CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor}; +use crate::render_resource::{ + CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor, VertexBufferDescriptor, +}; use bevy_utils::HashMap; use std::hash::Hash; @@ -20,9 +22,10 @@ impl SpecializedPipelines { cache: &mut RenderPipelineCache, specialize_pipeline: &S, key: S::Key, + vbd: &VertexBufferDescriptor, ) -> CachedPipelineId { *self.cache.entry(key.clone()).or_insert_with(|| { - let descriptor = specialize_pipeline.specialize(key); + let descriptor = specialize_pipeline.specialize(key, vbd); cache.queue(descriptor) }) } @@ -30,5 +33,10 @@ impl SpecializedPipelines { pub trait SpecializedPipeline { type Key: Clone + Hash + PartialEq + Eq; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; + type VertexSpecialization; + fn specialize( + &self, + key: Self::Key, + vbd: &VertexBufferDescriptor, + ) -> RenderPipelineDescriptor; } diff --git a/pipelined/bevy_render2/src/render_resource/vertex_attributes.rs b/pipelined/bevy_render2/src/render_resource/vertex_attributes.rs new file mode 100644 index 00000000000000..64675620821db3 --- /dev/null +++ b/pipelined/bevy_render2/src/render_resource/vertex_attributes.rs @@ -0,0 +1,183 @@ +use crate::render_resource::{VertexAttribute, VertexFormat}; +use std::marker::PhantomData; + +/// Describes a vertex attribute buffer descriptor. +#[derive(Debug, Eq, PartialEq)] +pub struct VertexBufferDescriptor { + attributes: Vec, + _phantom: PhantomData, +} + +// Don't derive `Default` because it would require `T: Default` +impl Default for VertexBufferDescriptor { + fn default() -> Self { + Self { + attributes: Vec::default(), + _phantom: PhantomData, + } + } +} + +// Don't derive `Clone` because it would require `T: Clone` +impl Clone for VertexBufferDescriptor { + fn clone(&self) -> Self { + Self { + attributes: self.attributes.clone(), + _phantom: PhantomData, + } + } +} + +impl VertexBufferDescriptor { + /// Create a populated vertex attribute buffer descriptor. + pub fn new(attributes: Vec) -> Self { + Self { + attributes, + _phantom: PhantomData, + } + } + + /// Convert into a descriptor for another type. + /// + /// Use this instead of `From`/`Into` to avoid trait implementation conflicts. + pub fn convert(self) -> VertexBufferDescriptor { + VertexBufferDescriptor::::new(self.attributes) + } + + /// Push a vertex attribute descriptor to the end of the list. + pub fn push(&mut self, name: &str, format: VertexFormat, shader_location: u32) { + let offset = if let Some(attribute) = self.attributes.last() { + attribute.offset + attribute.format.size() + } else { + 0 + }; + + self.attributes.push(VertexDescriptor { + name: name.to_string(), + format, + offset, + shader_location, + }); + } + + /// Get the vertex layout for a named attribute. + pub fn get_attribute_layout(&self, name: &str) -> Option<&VertexDescriptor> { + self.attributes + .iter() + .find(|attribute| attribute.name == name) + } + + /// Create a list of vertex attributes suitable for `wgpu`. + pub fn to_raw(&self) -> Vec { + self.attributes + .iter() + .map(|attribute| attribute.into()) + .collect() + } + + /// Get the buffer stride (size) of this vertex attribute buffer descriptor. + pub fn stride(&self) -> u64 { + self.attributes + .iter() + .fold(0, |acc, attribute| acc + attribute.format.size()) + } +} + +/// Describes a vertex attribute descriptor. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VertexDescriptor { + pub name: String, + pub format: VertexFormat, + pub offset: u64, + pub shader_location: u32, +} + +impl From<&VertexDescriptor> for VertexAttribute { + fn from(value: &VertexDescriptor) -> Self { + Self { + format: value.format, + offset: value.offset, + shader_location: value.shader_location, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let attributes = VertexBufferDescriptor::<()>::new(vec![ + VertexDescriptor { + name: "position".to_string(), + format: VertexFormat::Float32x3, + offset: 0, + shader_location: 0, + }, + VertexDescriptor { + name: "normal".to_string(), + format: VertexFormat::Float32x3, + offset: 12, + shader_location: 1, + }, + VertexDescriptor { + name: "uv".to_string(), + format: VertexFormat::Float32x2, + offset: 24, + shader_location: 2, + }, + ]); + + assert_eq!(attributes.stride(), 32); + assert_eq!( + attributes.get_attribute_layout("normal"), + Some(&VertexDescriptor { + name: "normal".to_string(), + format: VertexFormat::Float32x3, + offset: 12, + shader_location: 1, + }) + ); + assert!(attributes.get_attribute_layout("foo").is_none()); + } + + #[test] + fn test_push() { + let mut attributes = VertexBufferDescriptor::<()>::default(); + + attributes.push("position", VertexFormat::Float32x3, 0); + attributes.push("normal", VertexFormat::Float32x3, 1); + attributes.push("uv", VertexFormat::Float32x2, 2); + + assert_eq!( + attributes, + VertexBufferDescriptor::new(vec![ + VertexDescriptor { + name: "position".to_string(), + format: VertexFormat::Float32x3, + offset: 0, + shader_location: 0, + }, + VertexDescriptor { + name: "normal".to_string(), + format: VertexFormat::Float32x3, + offset: 12, + shader_location: 1, + }, + VertexDescriptor { + name: "uv".to_string(), + format: VertexFormat::Float32x2, + offset: 24, + shader_location: 2, + }, + ]) + ); + assert_eq!(attributes.stride(), 32); + assert_eq!( + attributes.get_attribute_layout("normal"), + Some(&attributes.attributes[1]) + ); + assert!(attributes.get_attribute_layout("foo").is_none()); + } +} diff --git a/pipelined/bevy_sprite2/src/render/mod.rs b/pipelined/bevy_sprite2/src/render/mod.rs index f88a0ceca4973e..2540683efc9066 100644 --- a/pipelined/bevy_sprite2/src/render/mod.rs +++ b/pipelined/bevy_sprite2/src/render/mod.rs @@ -91,23 +91,17 @@ pub struct SpritePipelineKey { impl SpecializedPipeline for SpritePipeline { type Key = SpritePipelineKey; + type VertexSpecialization = SpriteBatch; - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + fn specialize( + &self, + key: Self::Key, + vbd: &VertexBufferDescriptor, + ) -> 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, - }, - ], + attributes: vbd.to_raw(), }; let mut shader_defs = Vec::new(); if key.colored { @@ -292,6 +286,20 @@ pub struct SpriteBatch { colored: bool, } +impl SpriteBatch { + fn get_vertex_buffer_descriptor(&self) -> VertexBufferDescriptor { + let mut vbd = VertexBufferDescriptor::::default(); + + vbd.push("position", VertexFormat::Float32x3, 0); + vbd.push("uv", VertexFormat::Float32x2, 1); + if self.colored { + vbd.push("color", VertexFormat::Uint32, 2); + } + + vbd + } +} + pub fn prepare_sprites( mut commands: Commands, render_device: Res, @@ -456,6 +464,7 @@ pub fn queue_sprites( draw_functions: Res>, render_device: Res, mut sprite_meta: ResMut, + vertex_buffer_descriptor: Option>>, view_uniforms: Res, sprite_pipeline: Res, mut pipelines: ResMut>, @@ -475,16 +484,7 @@ pub fn queue_sprites( layout: &sprite_pipeline.view_layout, })); let draw_sprite_function = draw_functions.read().get_id::().unwrap(); - let pipeline = pipelines.specialize( - &mut pipeline_cache, - &sprite_pipeline, - SpritePipelineKey { colored: false }, - ); - let colored_pipeline = pipelines.specialize( - &mut pipeline_cache, - &sprite_pipeline, - SpritePipelineKey { colored: true }, - ); + for mut transparent_phase in views.iter_mut() { for (entity, batch) in sprite_batches.iter_mut() { image_bind_groups @@ -507,13 +507,25 @@ pub fn queue_sprites( layout: &sprite_pipeline.material_layout, }) }); + + let pipeline = { + let vbd = match vertex_buffer_descriptor.as_deref() { + Some(vbd) => vbd.clone(), + None => batch.get_vertex_buffer_descriptor(), + }; + pipelines.specialize( + &mut pipeline_cache, + &sprite_pipeline, + SpritePipelineKey { + colored: batch.colored, + }, + &vbd, + ) + }; + transparent_phase.add(Transparent2d { draw_function: draw_sprite_function, - pipeline: if batch.colored { - colored_pipeline - } else { - pipeline - }, + pipeline, entity, sort_key: FloatOrd(batch.z), });