diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a4b51b333b8..26021dfe436b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,13 @@ to view all changes since the `0.2.1` release. ## Unreleased ### Added - +- [Mesh overhaul with custom vertex attributes][616] for `Mesh` + - Any vertex attribute can now be added over `mesh.attributes.insert()`. For example: `mesh.attributes.insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), points.into())`. + - For missing attributes (requested by shader, but not defined by mesh), Bevy will provide a zero-filled fallback buffer. +- [Touch Input][696] +- [Do not depend on spirv on wasm32 target][689] +- [Another fast compile flag for macOS][552] +- [Mesh overhaul with custom vertex attributes][599] - [Introduce Mouse capture API][679] - [`bevy_input::touch`: implement touch input][696] - [D-pad support on MacOS][653] @@ -56,6 +62,12 @@ to view all changes since the `0.2.1` release. `Color::rgb` and `Color::rgba` will be converted to linear sRGB under-the-hood. - This allows drop-in use of colors from most applications. - New methods `Color::rgb_linear` and `Color::rgba_linear` will accept colors already in linear sRGB (the old behavior) + - Individual color-components must now be accessed through setters and getters: `.r`, `.g`, `.b`, `.a`, `.set_r`, `.set_g`, `.set_b`, `.set_a`, and the corresponding methods with the `*_linear` suffix. +- Breaking Change: [Mesh overhaul with custom vertex attributes][616] for `Mesh` + - Removed `VertexAttribute`, `Vertex`, `AsVertexBufferDescriptor`. + +- Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651] +- Breaking Change: Migrated to rodio 0.12, this means: - Individual color-components must now be accessed through setters and getters: `.r`, `.g`, `.b`, `.a`, `.set_r`, `.set_g`, `.set_b`, `.set_a`, and the corresponding methods with the `*_linear` suffix. - Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic: [#649][649], [#651][651] @@ -65,6 +77,9 @@ to view all changes since the `0.2.1` release. ### Fixed +[599]: https://github.com/bevyengine/bevy/pull/599 +[696]: https://github.com/bevyengine/bevy/pull/696 +[689]: https://github.com/bevyengine/bevy/pull/689 - [Properly update bind group ids when setting dynamic bindings][560] - [Properly exit the app on AppExit event][610] - [Fix FloatOrd hash being different for different NaN values][618] diff --git a/crates/bevy_derive/src/as_vertex_buffer_descriptor.rs b/crates/bevy_derive/src/as_vertex_buffer_descriptor.rs deleted file mode 100644 index fbe42bd422476..0000000000000 --- a/crates/bevy_derive/src/as_vertex_buffer_descriptor.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::modules::{get_modules, get_path}; -use inflector::Inflector; -use proc_macro::TokenStream; -use quote::{format_ident, quote}; -use syn::{ - parse::ParseStream, parse_macro_input, Data, DataStruct, DeriveInput, Field, Fields, Path, -}; - -#[derive(Default)] -struct VertexAttributes { - pub ignore: bool, - pub instance: bool, -} - -static VERTEX_ATTRIBUTE_NAME: &str = "vertex"; - -pub fn derive_as_vertex_buffer_descriptor(input: TokenStream) -> TokenStream { - let ast = parse_macro_input!(input as DeriveInput); - let modules = get_modules(&ast.attrs); - - let bevy_render_path: Path = get_path(&modules.bevy_render); - let fields = match &ast.data { - Data::Struct(DataStruct { - fields: Fields::Named(fields), - .. - }) => &fields.named, - _ => panic!("expected a struct with named fields"), - }; - let field_attributes = fields - .iter() - .map(|field| { - ( - field, - field - .attrs - .iter() - .find(|a| *a.path.get_ident().as_ref().unwrap() == VERTEX_ATTRIBUTE_NAME) - .map_or_else(VertexAttributes::default, |a| { - syn::custom_keyword!(ignore); - let mut vertex_attributes = VertexAttributes::default(); - a.parse_args_with(|input: ParseStream| { - if input.parse::>()?.is_some() { - vertex_attributes.ignore = true; - return Ok(()); - } - Ok(()) - }) - .expect("invalid 'vertex' attribute format"); - - vertex_attributes - }), - ) - }) - .collect::>(); - - let struct_name = &ast.ident; - - let mut vertex_buffer_field_names_pascal = Vec::new(); - let mut vertex_buffer_field_types = Vec::new(); - for (f, attrs) in field_attributes.iter() { - if attrs.ignore { - continue; - } - - vertex_buffer_field_types.push(&f.ty); - let pascal_field = f.ident.as_ref().unwrap().to_string().to_pascal_case(); - vertex_buffer_field_names_pascal.push(if attrs.instance { - format!("I_{}_{}", struct_name, pascal_field) - } else { - format!("{}_{}", struct_name, pascal_field) - }); - } - - let struct_name_string = struct_name.to_string(); - let struct_name_uppercase = struct_name_string.to_uppercase(); - let vertex_buffer_descriptor_ident = - format_ident!("{}_VERTEX_BUFFER_DESCRIPTOR", struct_name_uppercase); - - TokenStream::from(quote! { - static #vertex_buffer_descriptor_ident: #bevy_render_path::once_cell::sync::Lazy<#bevy_render_path::pipeline::VertexBufferDescriptor> = - #bevy_render_path::once_cell::sync::Lazy::new(|| { - use #bevy_render_path::pipeline::{VertexFormat, AsVertexFormats, VertexAttributeDescriptor}; - - let mut vertex_formats: Vec<(&str,&[VertexFormat])> = vec![ - #((#vertex_buffer_field_names_pascal, <#vertex_buffer_field_types>::as_vertex_formats()),)* - ]; - - let mut shader_location = 0; - let mut offset = 0; - let vertex_attribute_descriptors = vertex_formats.drain(..).map(|(name, formats)| { - formats.iter().enumerate().map(|(i, format)| { - let size = format.get_size(); - let formatted_name = if formats.len() > 1 { - format!("{}_{}", name, i) - } else { - format!("{}", name) - }; - let descriptor = VertexAttributeDescriptor { - name: formatted_name.into(), - offset, - format: *format, - shader_location, - }; - offset += size; - shader_location += 1; - descriptor - }).collect::>() - }).flatten().collect::>(); - - #bevy_render_path::pipeline::VertexBufferDescriptor { - attributes: vertex_attribute_descriptors, - name: #struct_name_string.into(), - step_mode: #bevy_render_path::pipeline::InputStepMode::Instance, - stride: offset, - } - }); - - impl #bevy_render_path::pipeline::AsVertexBufferDescriptor for #struct_name { - fn as_vertex_buffer_descriptor() -> &'static #bevy_render_path::pipeline::VertexBufferDescriptor { - &#vertex_buffer_descriptor_ident - } - } - }) -} diff --git a/crates/bevy_derive/src/lib.rs b/crates/bevy_derive/src/lib.rs index fe3a6a96db77c..0de7392d4626f 100644 --- a/crates/bevy_derive/src/lib.rs +++ b/crates/bevy_derive/src/lib.rs @@ -1,7 +1,6 @@ extern crate proc_macro; mod app_plugin; -mod as_vertex_buffer_descriptor; mod bytes; mod modules; mod render_resource; @@ -45,12 +44,6 @@ pub fn derive_shader_defs(input: TokenStream) -> TokenStream { shader_defs::derive_shader_defs(input) } -/// Derives the AsVertexBufferDescriptor trait. -#[proc_macro_derive(AsVertexBufferDescriptor, attributes(vertex, as_crate))] -pub fn derive_as_vertex_buffer_descriptor(input: TokenStream) -> TokenStream { - as_vertex_buffer_descriptor::derive_as_vertex_buffer_descriptor(input) -} - /// Generates a dynamic plugin entry point function for the given `Plugin` type. #[proc_macro_derive(DynamicPlugin)] pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream { diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 2418299f4f861..1e5e2251d5b7c 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -4,7 +4,7 @@ use bevy_ecs::{bevy_utils::BoxedFuture, World, WorldBuilderSource}; use bevy_math::Mat4; use bevy_pbr::prelude::{PbrComponents, StandardMaterial}; use bevy_render::{ - mesh::{Indices, Mesh, VertexAttribute}, + mesh::{Indices, Mesh, VertexAttributeValues}, pipeline::PrimitiveTopology, prelude::{Color, Texture}, texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat}, @@ -20,7 +20,7 @@ use gltf::{ Primitive, }; use image::{GenericImageView, ImageFormat}; -use std::path::Path; +use std::{borrow::Cow, path::Path}; use thiserror::Error; /// An error that occurs when loading a GLTF file @@ -88,23 +88,26 @@ async fn load_gltf<'a, 'b>( if let Some(vertex_attribute) = reader .read_positions() - .map(|v| VertexAttribute::position(v.collect())) + .map(|v| VertexAttributeValues::Float3(v.collect())) { - mesh.attributes.push(vertex_attribute); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), vertex_attribute); } if let Some(vertex_attribute) = reader .read_normals() - .map(|v| VertexAttribute::normal(v.collect())) + .map(|v| VertexAttributeValues::Float3(v.collect())) { - mesh.attributes.push(vertex_attribute); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), vertex_attribute); } if let Some(vertex_attribute) = reader .read_tex_coords(0) - .map(|v| VertexAttribute::uv(v.into_f32().collect())) + .map(|v| VertexAttributeValues::Float2(v.into_f32().collect())) { - mesh.attributes.push(vertex_attribute); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), vertex_attribute); } if let Some(indices) = reader.read_indices() { diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 303055b124631..99a9f710bd71b 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -1,7 +1,7 @@ use crate::{ pipeline::{ PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization, - VertexBufferDescriptors, + VERTEX_FALLBACK_LAYOUT_NAME, }, renderer::{ BindGroup, BindGroupId, BufferId, BufferUsage, RenderResource, RenderResourceBinding, @@ -131,7 +131,6 @@ pub struct DrawContext<'a> { pub shaders: ResMut<'a, Assets>, pub pipeline_compiler: ResMut<'a, PipelineCompiler>, pub render_resource_context: Res<'a, Box>, - pub vertex_buffer_descriptors: Res<'a, VertexBufferDescriptors>, pub shared_buffers: Res<'a, SharedBuffers>, pub current_pipeline: Option>, } @@ -143,7 +142,6 @@ impl<'a> UnsafeClone for DrawContext<'a> { shaders: self.shaders.unsafe_clone(), pipeline_compiler: self.pipeline_compiler.unsafe_clone(), render_resource_context: self.render_resource_context.unsafe_clone(), - vertex_buffer_descriptors: self.vertex_buffer_descriptors.unsafe_clone(), shared_buffers: self.shared_buffers.unsafe_clone(), current_pipeline: self.current_pipeline.clone(), } @@ -166,7 +164,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext { resources.borrow_mut::>(); resources.borrow_mut::(); resources.borrow::>(); - resources.borrow::(); resources.borrow::(); } @@ -175,7 +172,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext { resources.release_mut::>(); resources.release_mut::(); resources.release::>(); - resources.release::(); resources.release::(); } @@ -205,9 +201,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext { render_resource_context: Res::new( resources.get_unsafe_ref::>(ResourceIndex::Global), ), - vertex_buffer_descriptors: Res::new( - resources.get_unsafe_ref::(ResourceIndex::Global), - ), shared_buffers: Res::new( resources.get_unsafe_ref::(ResourceIndex::Global), ), @@ -221,7 +214,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext { access.add_write(TypeId::of::>()); access.add_write(TypeId::of::()); access.add_read(TypeId::of::>()); - access.add_read(TypeId::of::()); access.add_read(TypeId::of::()); access } @@ -262,7 +254,6 @@ impl<'a> DrawContext<'a> { &mut self.pipelines, &mut self.shaders, pipeline_handle, - &self.vertex_buffer_descriptors, specialization, ) }; @@ -358,18 +349,21 @@ impl<'a> DrawContext<'a> { let layout = pipeline_descriptor .get_layout() .ok_or(DrawError::PipelineHasNoLayout)?; - for (slot, vertex_buffer_descriptor) in layout.vertex_buffer_descriptors.iter().enumerate() - { - for bindings in render_resource_bindings.iter() { - if let Some((vertex_buffer, index_buffer)) = - bindings.get_vertex_buffer(&vertex_buffer_descriptor.name) - { - draw.set_vertex_buffer(slot as u32, vertex_buffer, 0); - if let Some(index_buffer) = index_buffer { - draw.set_index_buffer(index_buffer, 0); - } - - break; + // figure out if the fallback buffer is needed + let need_fallback_buffer = layout + .vertex_buffer_descriptors + .iter() + .any(|x| x.name == VERTEX_FALLBACK_LAYOUT_NAME); + for bindings in render_resource_bindings.iter() { + if let Some(index_buffer) = bindings.index_buffer { + draw.set_index_buffer(index_buffer, 0); + } + if let Some(main_vertex_buffer) = bindings.vertex_attribute_buffer { + draw.set_vertex_buffer(0, main_vertex_buffer, 0); + } + if need_fallback_buffer { + if let Some(fallback_vertex_buffer) = bindings.vertex_fallback_buffer { + draw.set_vertex_buffer(1, fallback_vertex_buffer, 0); } } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index a1dc9ea65b284..c96d6416a77fa 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -38,7 +38,7 @@ use camera::{ }; use pipeline::{ DynamicBinding, IndexFormat, PipelineCompiler, PipelineDescriptor, PipelineSpecialization, - PrimitiveTopology, ShaderSpecialization, VertexBufferDescriptors, + PrimitiveTopology, ShaderSpecialization, }; use render_graph::{ base::{self, BaseRenderGraphBuilder, BaseRenderGraphConfig}, @@ -119,7 +119,6 @@ impl Plugin for RenderPlugin { .init_resource::() .init_resource::() .init_resource::() - .init_resource::() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index 3500f0e04b436..6dfbea9bd8d69 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -1,9 +1,5 @@ -use super::Vertex; use crate::{ - pipeline::{ - AsVertexBufferDescriptor, PrimitiveTopology, RenderPipelines, VertexBufferDescriptor, - VertexBufferDescriptors, VertexFormat, - }, + pipeline::{PrimitiveTopology, RenderPipelines, VertexFormat}, renderer::{BufferInfo, BufferUsage, RenderResourceContext, RenderResourceId}, }; use bevy_app::prelude::{EventReader, Events}; @@ -12,12 +8,14 @@ use bevy_core::AsBytes; use bevy_ecs::{Local, Query, Res, ResMut}; use bevy_math::*; use bevy_type_registry::TypeUuid; -use bevy_utils::HashSet; use std::borrow::Cow; -use thiserror::Error; -pub const VERTEX_BUFFER_ASSET_INDEX: usize = 0; -pub const INDEX_BUFFER_ASSET_INDEX: usize = 1; +use crate::pipeline::{InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor}; +use bevy_utils::HashMap; + +pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; +pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; +pub const VERTEX_FALLBACK_BUFFER_ID: u64 = 20; #[derive(Clone, Debug)] pub enum VertexAttributeValues { Float(Vec), @@ -62,49 +60,28 @@ impl From<&VertexAttributeValues> for VertexFormat { } } -#[derive(Debug)] -pub struct VertexAttribute { - pub name: Cow<'static, str>, - pub values: VertexAttributeValues, -} - -impl VertexAttribute { - pub const NORMAL: &'static str = "Vertex_Normal"; - pub const POSITION: &'static str = "Vertex_Position"; - pub const UV: &'static str = "Vertex_Uv"; - - pub fn position(positions: Vec<[f32; 3]>) -> Self { - VertexAttribute { - name: Self::POSITION.into(), - values: VertexAttributeValues::Float3(positions), - } +impl From> for VertexAttributeValues { + fn from(vec: Vec) -> Self { + VertexAttributeValues::Float(vec) } +} - pub fn normal(normals: Vec<[f32; 3]>) -> Self { - VertexAttribute { - name: Self::NORMAL.into(), - values: VertexAttributeValues::Float3(normals), - } +impl From> for VertexAttributeValues { + fn from(vec: Vec<[f32; 2]>) -> Self { + VertexAttributeValues::Float2(vec) } +} - pub fn uv(uvs: Vec<[f32; 2]>) -> Self { - VertexAttribute { - name: Self::UV.into(), - values: VertexAttributeValues::Float2(uvs), - } +impl From> for VertexAttributeValues { + fn from(vec: Vec<[f32; 3]>) -> Self { + VertexAttributeValues::Float3(vec) } } -#[derive(Error, Debug)] -pub enum MeshToVertexBufferError { - #[error("VertexBufferDescriptor requires a VertexBufferAttribute this Mesh does not contain.")] - MissingVertexAttribute { attribute_name: Cow<'static, str> }, - #[error("Mesh VertexAttribute VertexFormat is incompatible with VertexBufferDescriptor VertexAttribute VertexFormat.")] - IncompatibleVertexAttributeFormat { - attribute_name: Cow<'static, str>, - descriptor_format: VertexFormat, - mesh_format: VertexFormat, - }, +impl From> for VertexAttributeValues { + fn from(vec: Vec<[f32; 4]>) -> Self { + VertexAttributeValues::Float4(vec) + } } #[derive(Debug)] @@ -112,61 +89,34 @@ pub enum Indices { U16(Vec), U32(Vec), } +// TODO: allow values to be unloaded after been submitting to the GPU to conserve memory +pub type VertexAttributesHashMap = HashMap, VertexAttributeValues>; #[derive(Debug, TypeUuid)] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] pub struct Mesh { pub primitive_topology: PrimitiveTopology, - pub attributes: Vec, + /// `bevy_utils::HashMap` with all defined vertex attributes (Positions, Normals, ...) for this mesh. Attribute name maps to attribute values. + pub attributes: VertexAttributesHashMap, pub indices: Option, + /// The layout of the attributes in the GPU buffer without `shader_location`. `None` will indicate that no data has been uploaded to the GPU yet. + pub attribute_buffer_descriptor_reference: Option, } impl Mesh { + pub const ATTRIBUTE_NORMAL: &'static str = "Vertex_Normal"; + pub const ATTRIBUTE_POSITION: &'static str = "Vertex_Position"; + pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv"; + pub fn new(primitive_topology: PrimitiveTopology) -> Self { Mesh { primitive_topology, - attributes: Vec::new(), + attributes: Default::default(), indices: None, + attribute_buffer_descriptor_reference: Default::default(), } } - pub fn get_vertex_buffer_bytes( - &self, - vertex_buffer_descriptor: &VertexBufferDescriptor, - fill_missing_attributes: bool, - ) -> Result, MeshToVertexBufferError> { - let length = self.attributes.first().map(|a| a.values.len()).unwrap_or(0); - let mut bytes = vec![0; vertex_buffer_descriptor.stride as usize * length]; - - for vertex_attribute in vertex_buffer_descriptor.attributes.iter() { - match self - .attributes - .iter() - .find(|a| vertex_attribute.name == a.name) - { - Some(mesh_attribute) => { - let attribute_bytes = mesh_attribute.values.get_bytes(); - let attribute_size = vertex_attribute.format.get_size() as usize; - for (i, vertex_slice) in attribute_bytes.chunks(attribute_size).enumerate() { - let vertex_offset = vertex_buffer_descriptor.stride as usize * i; - let attribute_offset = vertex_offset + vertex_attribute.offset as usize; - bytes[attribute_offset..attribute_offset + attribute_size] - .copy_from_slice(vertex_slice); - } - } - None => { - if !fill_missing_attributes { - return Err(MeshToVertexBufferError::MissingVertexAttribute { - attribute_name: vertex_attribute.name.clone(), - }); - } - } - } - } - - Ok(bytes) - } - pub fn get_index_buffer_bytes(&self) -> Option> { self.indices.as_ref().map(|indices| match &indices { Indices::U16(indices) => indices.as_slice().as_bytes().to_vec(), @@ -177,10 +127,11 @@ impl Mesh { /// Generation for some primitive shape meshes. pub mod shape { - use super::{Indices, Mesh, VertexAttribute}; + use super::{Indices, Mesh}; use crate::pipeline::PrimitiveTopology; use bevy_math::*; use hexasphere::Hexasphere; + use std::borrow::Cow; /// A cube. #[derive(Debug)] @@ -249,15 +200,15 @@ pub mod shape { 20, 21, 22, 22, 23, 20, // back ]); - Mesh { - primitive_topology: PrimitiveTopology::TriangleList, - attributes: vec![ - VertexAttribute::position(positions), - VertexAttribute::normal(normals), - VertexAttribute::uv(uvs), - ], - indices: Some(indices), - } + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into()); + mesh.indices = Some(indices); + mesh } } @@ -339,24 +290,24 @@ pub mod shape { let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]); - let mut positions = Vec::new(); - let mut normals = Vec::new(); - let mut uvs = Vec::new(); + let mut positions = Vec::<[f32; 3]>::new(); + let mut normals = Vec::<[f32; 3]>::new(); + let mut uvs = Vec::<[f32; 2]>::new(); for (position, normal, uv) in vertices.iter() { positions.push(*position); normals.push(*normal); uvs.push(*uv); } - Mesh { - primitive_topology: PrimitiveTopology::TriangleList, - attributes: vec![ - VertexAttribute::position(positions), - VertexAttribute::normal(normals), - VertexAttribute::uv(uvs), - ], - indices: Some(indices), - } + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.indices = Some(indices); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into()); + mesh } } @@ -389,15 +340,15 @@ pub mod shape { uvs.push(*uv); } - Mesh { - primitive_topology: PrimitiveTopology::TriangleList, - attributes: vec![ - VertexAttribute::position(positions), - VertexAttribute::normal(normals), - VertexAttribute::uv(uvs), - ], - indices: Some(indices), - } + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.indices = Some(indices); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into()); + mesh } } @@ -463,62 +414,53 @@ pub mod shape { let indices = Indices::U32(indices); - Mesh { - primitive_topology: PrimitiveTopology::TriangleList, - attributes: vec![ - VertexAttribute::position(points), - VertexAttribute::normal(normals), - VertexAttribute::uv(uvs), - ], - indices: Some(indices), - } + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); + mesh.indices = Some(indices); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), points.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into()); + mesh.attributes + .insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into()); + mesh } } } -fn remove_current_mesh_resources( +fn remove_resource_save( render_resource_context: &dyn RenderResourceContext, handle: &Handle, + index: u64, ) { if let Some(RenderResourceId::Buffer(buffer)) = - render_resource_context.get_asset_resource(&handle, VERTEX_BUFFER_ASSET_INDEX) - { - render_resource_context.remove_buffer(buffer); - render_resource_context.remove_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX); - } - if let Some(RenderResourceId::Buffer(buffer)) = - render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX) + render_resource_context.get_asset_resource(&handle, index) { render_resource_context.remove_buffer(buffer); - render_resource_context.remove_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX); + render_resource_context.remove_asset_resource(handle, index); } } +fn remove_current_mesh_resources( + render_resource_context: &dyn RenderResourceContext, + handle: &Handle, +) { + remove_resource_save(render_resource_context, handle, VERTEX_ATTRIBUTE_BUFFER_ID); + remove_resource_save(render_resource_context, handle, VERTEX_FALLBACK_BUFFER_ID); + remove_resource_save(render_resource_context, handle, INDEX_BUFFER_ASSET_INDEX); +} #[derive(Default)] pub struct MeshResourceProviderState { mesh_event_reader: EventReader>, - vertex_buffer_descriptor: Option<&'static VertexBufferDescriptor>, } pub fn mesh_resource_provider_system( mut state: Local, render_resource_context: Res>, - meshes: Res>, - mut vertex_buffer_descriptors: ResMut, + mut meshes: ResMut>, mesh_events: Res>>, mut query: Query<(&Handle, &mut RenderPipelines)>, ) { - let vertex_buffer_descriptor = match state.vertex_buffer_descriptor { - Some(value) => value, - None => { - // TODO: allow pipelines to specialize on vertex_buffer_descriptor and index_format - let vertex_buffer_descriptor = Vertex::as_vertex_buffer_descriptor(); - vertex_buffer_descriptors.set(vertex_buffer_descriptor.clone()); - state.vertex_buffer_descriptor = Some(vertex_buffer_descriptor); - vertex_buffer_descriptor - } - }; - let mut changed_meshes = HashSet::>::default(); + let mut changed_meshes = bevy_utils::HashSet::>::default(); let render_resource_context = &**render_resource_context; for event in state.mesh_event_reader.iter(&mesh_events) { match event { @@ -538,126 +480,151 @@ pub fn mesh_resource_provider_system( } } + // update changed mesh data for changed_mesh_handle in changed_meshes.iter() { - if let Some(mesh) = meshes.get(changed_mesh_handle) { - let vertex_bytes = mesh - .get_vertex_buffer_bytes(&vertex_buffer_descriptor, true) - .unwrap(); - // TODO: use a staging buffer here - let vertex_buffer = render_resource_context.create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::VERTEX, - ..Default::default() - }, - &vertex_bytes, - ); - - let index_bytes = mesh.get_index_buffer_bytes().unwrap(); + if let Some(mesh) = meshes.get_mut(changed_mesh_handle) { + // TODO: check for individual buffer changes in non-interleaved mode let index_buffer = render_resource_context.create_buffer_with_data( BufferInfo { buffer_usage: BufferUsage::INDEX, ..Default::default() }, - &index_bytes, + &mesh.get_index_buffer_bytes().unwrap(), ); render_resource_context.set_asset_resource( changed_mesh_handle, - RenderResourceId::Buffer(vertex_buffer), - VERTEX_BUFFER_ASSET_INDEX, + RenderResourceId::Buffer(index_buffer), + INDEX_BUFFER_ASSET_INDEX, ); + + // Vertex buffer + let vertex_count = attributes_count_vertices(&mesh.attributes).unwrap(); + let interleaved_buffer = + attributes_to_vertex_buffer_data(&mesh.attributes, vertex_count); + + mesh.attribute_buffer_descriptor_reference = Some(interleaved_buffer.1); render_resource_context.set_asset_resource( changed_mesh_handle, - RenderResourceId::Buffer(index_buffer), - INDEX_BUFFER_ASSET_INDEX, + RenderResourceId::Buffer(render_resource_context.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::VERTEX, + ..Default::default() + }, + &interleaved_buffer.0, + )), + VERTEX_ATTRIBUTE_BUFFER_ID, + ); + + // Fallback buffer + // TODO: can be done with a 1 byte buffer + zero stride? + render_resource_context.set_asset_resource( + changed_mesh_handle, + RenderResourceId::Buffer(render_resource_context.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::VERTEX, + ..Default::default() + }, + &vec![0; (vertex_count * VertexFormat::Float4.get_size() as u32) as usize], + )), + VERTEX_FALLBACK_BUFFER_ID, ); } } + // handover buffers to pipeline // TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target for (handle, mut render_pipelines) in query.iter_mut() { if let Some(mesh) = meshes.get(handle) { for render_pipeline in render_pipelines.pipelines.iter_mut() { render_pipeline.specialization.primitive_topology = mesh.primitive_topology; } - } - if let Some(RenderResourceId::Buffer(vertex_buffer)) = - render_resource_context.get_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX) - { - render_pipelines.bindings.set_vertex_buffer( - "Vertex", - vertex_buffer, - render_resource_context - .get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX) - .and_then(|r| { - if let RenderResourceId::Buffer(buffer) = r { - Some(buffer) - } else { - None - } - }), - ); + if let Some(RenderResourceId::Buffer(index_buffer_resource)) = + render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX) + { + // set index buffer into binding + render_pipelines + .bindings + .set_index_buffer(index_buffer_resource); + } + + if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) = + render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID) + { + // set index buffer into binding + render_pipelines.bindings.vertex_attribute_buffer = + Some(vertex_attribute_buffer_resource); + } + if let Some(RenderResourceId::Buffer(vertex_attribute_fallback_resource)) = + render_resource_context.get_asset_resource(handle, VERTEX_FALLBACK_BUFFER_ID) + { + // set index buffer into binding + render_pipelines.bindings.vertex_fallback_buffer = + Some(vertex_attribute_fallback_resource); + } } } } -#[cfg(test)] -mod tests { - use super::{AsVertexBufferDescriptor, Mesh, VertexAttribute}; - use crate::{mesh::Vertex, pipeline::PrimitiveTopology}; - use bevy_core::AsBytes; - - #[test] - fn test_get_vertex_bytes() { - let vertices = &[ - ([0., 0., 0.], [1., 1., 1.], [2., 2.]), - ([3., 3., 3.], [4., 4., 4.], [5., 5.]), - ([6., 6., 6.], [7., 7., 7.], [8., 8.]), - ]; - - let mut positions = Vec::new(); - let mut normals = Vec::new(); - let mut uvs = Vec::new(); - for (position, normal, uv) in vertices.iter() { - positions.push(*position); - normals.push(*normal); - uvs.push(*uv); +pub fn attributes_count_vertices(attributes: &VertexAttributesHashMap) -> Option { + let mut vertex_count: Option = None; + for (attribute_name, attribute_data) in attributes { + let attribute_len = attribute_data.len(); + if let Some(previous_vertex_count) = vertex_count { + assert_eq!(previous_vertex_count, attribute_len as u32, + "Attribute {} has a different vertex count ({}) than other attributes ({}) in this mesh.", attribute_name, attribute_len, previous_vertex_count); } - - let mesh = Mesh { - primitive_topology: PrimitiveTopology::TriangleStrip, - attributes: vec![ - VertexAttribute::position(positions), - VertexAttribute::normal(normals), - VertexAttribute::uv(uvs), - ], - indices: None, - }; - - let expected_vertices = &[ - Vertex { - position: [0., 0., 0.], - normal: [1., 1., 1.], - uv: [2., 2.], - }, - Vertex { - position: [3., 3., 3.], - normal: [4., 4., 4.], - uv: [5., 5.], - }, - Vertex { - position: [6., 6., 6.], - normal: [7., 7., 7.], - uv: [8., 8.], + vertex_count = Some(attribute_len as u32); + } + vertex_count +} +pub fn attributes_to_vertex_buffer_data( + attributes: &VertexAttributesHashMap, + vertex_count: u32, +) -> (Vec, VertexBufferDescriptor) { + // get existing attribute data as bytes and generate attribute descriptor + let mut attributes_gpu_ready = Vec::<(VertexAttributeDescriptor, &[u8])>::default(); + let mut accumulated_offset = 0; + let mut attributes_sorted: Vec<_> = attributes.iter().collect(); + attributes_sorted.sort_by(|a, b| a.0.cmp(b.0)); + for attribute_data in attributes_sorted { + // TODO: allow for custom converter here + let vertex_format = VertexFormat::from(attribute_data.1); + attributes_gpu_ready.push(( + // this serves as a reference and is not supposed to be used directly. + VertexAttributeDescriptor { + name: attribute_data.0.clone(), + offset: accumulated_offset, + format: vertex_format, + shader_location: 0, }, - ]; - - let descriptor = Vertex::as_vertex_buffer_descriptor(); - assert_eq!( - mesh.get_vertex_buffer_bytes(descriptor, true).unwrap(), - expected_vertices.as_bytes(), - "buffer bytes are equal" - ); + attribute_data.1.get_bytes(), + )); + accumulated_offset += vertex_format.get_size(); } + let mut attributes_interleaved_buffer = Vec::::default(); + + // bundle into interleaved buffers + for vertex_index in 0..vertex_count { + let vertex_index = vertex_index as usize; + for (attribute_descriptor, attributes_bytes) in &attributes_gpu_ready { + let stride = attribute_descriptor.format.get_size() as usize; + // insert one element + attributes_interleaved_buffer + .extend(&attributes_bytes[vertex_index * stride..vertex_index * stride + stride]); + } + } + + let vertex_buffer_descriptor_reference = VertexBufferDescriptor { + name: Default::default(), + stride: accumulated_offset, + step_mode: InputStepMode::Vertex, + attributes: attributes_gpu_ready.iter().map(|x| x.0.clone()).collect(), + }; + + ( + attributes_interleaved_buffer, + vertex_buffer_descriptor_reference, + ) } diff --git a/crates/bevy_render/src/mesh/mod.rs b/crates/bevy_render/src/mesh/mod.rs index f9d33b4891c37..1db18c486545b 100644 --- a/crates/bevy_render/src/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mod.rs @@ -1,6 +1,4 @@ #[allow(clippy::module_inception)] mod mesh; -mod vertex; pub use mesh::*; -pub use vertex::*; diff --git a/crates/bevy_render/src/mesh/vertex.rs b/crates/bevy_render/src/mesh/vertex.rs deleted file mode 100644 index f8e4530c56609..0000000000000 --- a/crates/bevy_render/src/mesh/vertex.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::pipeline::AsVertexBufferDescriptor; -use bevy_core::Byteable; - -#[repr(C)] -#[derive(Debug, Clone, Copy, AsVertexBufferDescriptor)] -#[as_crate(bevy_render)] -pub struct Vertex { - pub position: [f32; 3], - pub normal: [f32; 3], - pub uv: [f32; 2], -} - -// SAFE: Vertex is repr(C) containing primitives -unsafe impl Byteable for Vertex {} diff --git a/crates/bevy_render/src/pipeline/pipeline.rs b/crates/bevy_render/src/pipeline/pipeline.rs index 06cff5dcf0e03..0328dee02196e 100644 --- a/crates/bevy_render/src/pipeline/pipeline.rs +++ b/crates/bevy_render/src/pipeline/pipeline.rs @@ -4,7 +4,7 @@ use super::{ CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace, IndexFormat, PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor, }, - BindType, DynamicBinding, PipelineLayout, StencilStateDescriptor, VertexBufferDescriptors, + BindType, DynamicBinding, PipelineLayout, StencilStateDescriptor, }; use crate::{ shader::{Shader, ShaderStages}, @@ -131,7 +131,6 @@ impl PipelineDescriptor { &mut self, shaders: &Assets, bevy_conventions: bool, - vertex_buffer_descriptors: Option<&VertexBufferDescriptors>, dynamic_bindings: &[DynamicBinding], ) { let vertex_spirv = shaders.get(&self.shader_stages.vertex).unwrap(); @@ -147,9 +146,6 @@ impl PipelineDescriptor { } let mut layout = PipelineLayout::from_shader_layouts(&mut layouts); - if let Some(vertex_buffer_descriptors) = vertex_buffer_descriptors { - layout.sync_vertex_buffer_descriptors(vertex_buffer_descriptors); - } if !dynamic_bindings.is_empty() { // set binding uniforms to dynamic if render resource bindings use dynamic diff --git a/crates/bevy_render/src/pipeline/pipeline_compiler.rs b/crates/bevy_render/src/pipeline/pipeline_compiler.rs index 8bbe138d87169..6c1378711ac09 100644 --- a/crates/bevy_render/src/pipeline/pipeline_compiler.rs +++ b/crates/bevy_render/src/pipeline/pipeline_compiler.rs @@ -1,7 +1,9 @@ -use super::{ - state_descriptors::PrimitiveTopology, IndexFormat, PipelineDescriptor, VertexBufferDescriptors, -}; +use super::{state_descriptors::PrimitiveTopology, IndexFormat, PipelineDescriptor}; use crate::{ + pipeline::{ + InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat, + VERTEX_FALLBACK_LAYOUT_NAME, + }, renderer::RenderResourceContext, shader::{Shader, ShaderSource}, }; @@ -10,6 +12,7 @@ use bevy_property::{Properties, Property}; use bevy_utils::{HashMap, HashSet}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; #[derive(Clone, Eq, PartialEq, Debug, Properties)] pub struct PipelineSpecialization { @@ -17,6 +20,7 @@ pub struct PipelineSpecialization { pub primitive_topology: PrimitiveTopology, pub dynamic_bindings: Vec, pub index_format: IndexFormat, + pub mesh_attribute_layout: VertexBufferDescriptor, pub sample_count: u32, } @@ -28,6 +32,7 @@ impl Default for PipelineSpecialization { primitive_topology: Default::default(), dynamic_bindings: Default::default(), index_format: IndexFormat::Uint32, + mesh_attribute_layout: Default::default(), } } } @@ -137,7 +142,6 @@ impl PipelineCompiler { pipelines: &mut Assets, shaders: &mut Assets, source_pipeline: &Handle, - vertex_buffer_descriptors: &VertexBufferDescriptors, pipeline_specialization: &PipelineSpecialization, ) -> Handle { let source_descriptor = pipelines.get(source_pipeline).unwrap(); @@ -162,10 +166,62 @@ impl PipelineCompiler { specialized_descriptor.reflect_layout( shaders, true, - Some(vertex_buffer_descriptors), &pipeline_specialization.dynamic_bindings, ); + // create a vertex layout that provides all attributes from either the specialized vertex buffers or a zero buffer + let mut pipeline_layout = specialized_descriptor.layout.as_mut().unwrap(); + // the vertex buffer descriptor of the mesh + let mesh_vertex_buffer_descriptor = pipeline_specialization.mesh_attribute_layout.clone(); + + // the vertex buffer descriptor that will be used for this pipeline + let mut compiled_vertex_buffer_descriptor = VertexBufferDescriptor { + step_mode: InputStepMode::Vertex, + stride: mesh_vertex_buffer_descriptor.stride, + ..Default::default() + }; + + let mut fallback_vertex_buffer_descriptor = VertexBufferDescriptor { + name: Cow::Borrowed(VERTEX_FALLBACK_LAYOUT_NAME), + stride: VertexFormat::Float4.get_size(), //TODO: use smallest possible format + ..Default::default() + }; + for shader_vertex_attribute in pipeline_layout.vertex_buffer_descriptors.iter() { + let shader_vertex_attribute = shader_vertex_attribute + .attributes + .get(0) + .expect("Reflected layout has no attributes."); + + if let Some(target_vertex_attribute) = mesh_vertex_buffer_descriptor + .attributes + .iter() + .find(|x| x.name == shader_vertex_attribute.name) + { + // copy shader location from reflected layout + let mut compiled_vertex_attribute = target_vertex_attribute.clone(); + compiled_vertex_attribute.shader_location = shader_vertex_attribute.shader_location; + compiled_vertex_buffer_descriptor + .attributes + .push(compiled_vertex_attribute); + } else { + fallback_vertex_buffer_descriptor + .attributes + .push(VertexAttributeDescriptor { + name: Default::default(), + offset: 0, + format: shader_vertex_attribute.format, //TODO: use smallest possible format + shader_location: shader_vertex_attribute.shader_location, + }); + } + } + + //TODO: add other buffers (like instancing) here + let mut vertex_buffer_descriptors = Vec::::default(); + vertex_buffer_descriptors.push(compiled_vertex_buffer_descriptor); + if !fallback_vertex_buffer_descriptor.attributes.is_empty() { + vertex_buffer_descriptors.push(fallback_vertex_buffer_descriptor); + } + pipeline_layout.vertex_buffer_descriptors = vertex_buffer_descriptors; specialized_descriptor.sample_count = pipeline_specialization.sample_count; specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology; specialized_descriptor.index_format = pipeline_specialization.index_format; diff --git a/crates/bevy_render/src/pipeline/pipeline_layout.rs b/crates/bevy_render/src/pipeline/pipeline_layout.rs index 56861ed9058aa..7069e1dca411b 100644 --- a/crates/bevy_render/src/pipeline/pipeline_layout.rs +++ b/crates/bevy_render/src/pipeline/pipeline_layout.rs @@ -1,5 +1,5 @@ -use super::{BindGroupDescriptor, VertexBufferDescriptor, VertexBufferDescriptors}; -use crate::shader::{ShaderLayout, GL_VERTEX_INDEX}; +use super::{BindGroupDescriptor, VertexBufferDescriptor}; +use crate::shader::ShaderLayout; use bevy_utils::HashMap; use std::hash::Hash; @@ -67,27 +67,6 @@ impl PipelineLayout { vertex_buffer_descriptors, } } - - pub fn sync_vertex_buffer_descriptors( - &mut self, - vertex_buffer_descriptors: &VertexBufferDescriptors, - ) { - for vertex_buffer_descriptor in self.vertex_buffer_descriptors.iter_mut() { - if let Some(graph_descriptor) = - vertex_buffer_descriptors.get(&vertex_buffer_descriptor.name) - { - vertex_buffer_descriptor.sync_with_descriptor(graph_descriptor); - } else if vertex_buffer_descriptor.name == GL_VERTEX_INDEX { - // GL_VERTEX_INDEX is a special attribute set on our behalf - continue; - } else { - panic!( - "Encountered unsupported Vertex Buffer: {}", - vertex_buffer_descriptor.name - ); - } - } - } } #[derive(Hash, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] diff --git a/crates/bevy_render/src/pipeline/render_pipelines.rs b/crates/bevy_render/src/pipeline/render_pipelines.rs index c7317473fdd60..5cae2587daa9f 100644 --- a/crates/bevy_render/src/pipeline/render_pipelines.rs +++ b/crates/bevy_render/src/pipeline/render_pipelines.rs @@ -101,6 +101,11 @@ pub fn draw_render_pipelines_system( for pipeline in render_pipelines.pipelines.iter_mut() { pipeline.specialization.sample_count = msaa.samples; pipeline.specialization.index_format = index_format; + pipeline.specialization.mesh_attribute_layout = mesh + .attribute_buffer_descriptor_reference + .as_ref() + .unwrap() + .clone(); } for render_pipeline in render_pipelines.pipelines.iter() { @@ -123,6 +128,7 @@ pub fn draw_render_pipelines_system( draw_context .set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings]) .unwrap(); + if let Some(indices) = index_range.clone() { draw.draw_indexed(indices, 0, 0..1); } diff --git a/crates/bevy_render/src/pipeline/vertex_buffer_descriptor.rs b/crates/bevy_render/src/pipeline/vertex_buffer_descriptor.rs index ad4875e54d53b..96a136fcb97ca 100644 --- a/crates/bevy_render/src/pipeline/vertex_buffer_descriptor.rs +++ b/crates/bevy_render/src/pipeline/vertex_buffer_descriptor.rs @@ -1,10 +1,12 @@ use super::VertexFormat; -use bevy_utils::HashMap; -use std::borrow::Cow; - -pub use bevy_derive::AsVertexBufferDescriptor; - -#[derive(Clone, Debug, Eq, PartialEq)] +use bevy_property::Property; +use serde::{Deserialize, Serialize}; +use std::{ + borrow::Cow, + hash::{Hash, Hasher}, +}; + +#[derive(Clone, Debug, Eq, PartialEq, Default, Property, Serialize, Deserialize)] pub struct VertexBufferDescriptor { pub name: Cow<'static, str>, pub stride: u64, @@ -12,33 +14,33 @@ pub struct VertexBufferDescriptor { pub attributes: Vec, } +pub const VERTEX_FALLBACK_LAYOUT_NAME: &str = "Fallback"; impl VertexBufferDescriptor { - pub fn sync_with_descriptor(&mut self, descriptor: &VertexBufferDescriptor) { - for attribute in self.attributes.iter_mut() { - let descriptor_attribute = descriptor - .attributes - .iter() - .find(|a| a.name == attribute.name) - .unwrap_or_else(|| { - panic!( - "Encountered unsupported Vertex Buffer Attribute: {}", - attribute.name - ); - }); - attribute.offset = descriptor_attribute.offset; + pub fn new_from_attribute( + attribute: VertexAttributeDescriptor, + step_mode: InputStepMode, + ) -> VertexBufferDescriptor { + VertexBufferDescriptor { + name: attribute.name.clone(), + stride: attribute.format.get_size(), + step_mode, + attributes: vec![attribute.clone()], } - - self.stride = descriptor.stride; } } - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub enum InputStepMode { Vertex = 0, Instance = 1, } -#[derive(Clone, Debug, Hash, Eq, PartialEq)] +impl Default for InputStepMode { + fn default() -> Self { + InputStepMode::Vertex + } +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub struct VertexAttributeDescriptor { pub name: Cow<'static, str>, pub offset: u64, @@ -46,24 +48,9 @@ pub struct VertexAttributeDescriptor { pub shader_location: u32, } -#[derive(Debug, Default)] -pub struct VertexBufferDescriptors { - pub descriptors: HashMap, -} - -impl VertexBufferDescriptors { - pub fn set(&mut self, vertex_buffer_descriptor: VertexBufferDescriptor) { - self.descriptors.insert( - vertex_buffer_descriptor.name.to_string(), - vertex_buffer_descriptor, - ); - } - - pub fn get(&self, name: &str) -> Option<&VertexBufferDescriptor> { - self.descriptors.get(name) - } -} - -pub trait AsVertexBufferDescriptor { - fn as_vertex_buffer_descriptor() -> &'static VertexBufferDescriptor; +/// Internally, `bevy_render` uses hashes to identify vertex attribute names. +pub fn get_vertex_attribute_name_id(name: &str) -> u64 { + let mut hasher = bevy_utils::AHasher::default(); + hasher.write(&name.as_bytes()); + hasher.finish() } diff --git a/crates/bevy_render/src/pipeline/vertex_format.rs b/crates/bevy_render/src/pipeline/vertex_format.rs index b738e2858d983..750ea83c59a0e 100644 --- a/crates/bevy_render/src/pipeline/vertex_format.rs +++ b/crates/bevy_render/src/pipeline/vertex_format.rs @@ -1,6 +1,7 @@ use crate::Color; use bevy_math::{Mat4, Vec2, Vec3, Vec4}; -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +use serde::{Deserialize, Serialize}; +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub enum VertexFormat { Uchar2 = 1, Uchar4 = 3, diff --git a/crates/bevy_render/src/renderer/headless_render_resource_context.rs b/crates/bevy_render/src/renderer/headless_render_resource_context.rs index 196c050675bb7..c4c1335880ca6 100644 --- a/crates/bevy_render/src/renderer/headless_render_resource_context.rs +++ b/crates/bevy_render/src/renderer/headless_render_resource_context.rs @@ -15,7 +15,7 @@ use std::{ops::Range, sync::Arc}; pub struct HeadlessRenderResourceContext { buffer_info: Arc>>, texture_descriptors: Arc>>, - pub asset_resources: Arc>>, + pub asset_resources: Arc>>, } impl HeadlessRenderResourceContext { @@ -92,7 +92,7 @@ impl RenderResourceContext for HeadlessRenderResourceContext { &self, handle: HandleUntyped, render_resource: RenderResourceId, - index: usize, + index: u64, ) { self.asset_resources .write() @@ -102,7 +102,7 @@ impl RenderResourceContext for HeadlessRenderResourceContext { fn get_asset_resource_untyped( &self, handle: HandleUntyped, - index: usize, + index: u64, ) -> Option { self.asset_resources.write().get(&(handle, index)).cloned() } @@ -124,7 +124,7 @@ impl RenderResourceContext for HeadlessRenderResourceContext { fn create_shader_module_from_source(&self, _shader_handle: &Handle, _shader: &Shader) {} - fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: usize) { + fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: u64) { self.asset_resources.write().remove(&(handle, index)); } diff --git a/crates/bevy_render/src/renderer/render_resource/render_resource_bindings.rs b/crates/bevy_render/src/renderer/render_resource/render_resource_bindings.rs index 99dcdf07f5217..8e3e03199db1d 100644 --- a/crates/bevy_render/src/renderer/render_resource/render_resource_bindings.rs +++ b/crates/bevy_render/src/renderer/render_resource/render_resource_bindings.rs @@ -6,7 +6,6 @@ use crate::{ use bevy_asset::{Asset, Handle, HandleUntyped}; use bevy_utils::{HashMap, HashSet}; use std::{hash::Hash, ops::Range}; -use uuid::Uuid; #[derive(Clone, Eq, Debug)] pub enum RenderResourceBinding { @@ -104,11 +103,12 @@ pub enum BindGroupStatus { // PERF: if the bindings are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost #[derive(Eq, PartialEq, Debug, Default, Clone)] pub struct RenderResourceBindings { - // TODO: remove this. it shouldn't be needed anymore - pub id: RenderResourceBindingsId, bindings: HashMap, - // TODO: remove this - vertex_buffers: HashMap)>, + /// A Buffer that contains all attributes a mesh has defined + pub vertex_attribute_buffer: Option, + /// A Buffer that is filled with zeros that will be used for attributes required by the shader, but undefined by the mesh. + pub vertex_fallback_buffer: Option, + pub index_buffer: Option, bind_groups: HashMap, bind_group_descriptors: HashMap>, dirty_bind_groups: HashSet, @@ -139,25 +139,10 @@ impl RenderResourceBindings { for (name, binding) in render_resource_bindings.bindings.iter() { self.set(name, binding.clone()); } - - for (name, (vertex_buffer, index_buffer)) in render_resource_bindings.vertex_buffers.iter() - { - self.set_vertex_buffer(name, *vertex_buffer, *index_buffer); - } } - pub fn get_vertex_buffer(&self, name: &str) -> Option<(BufferId, Option)> { - self.vertex_buffers.get(name).cloned() - } - - pub fn set_vertex_buffer( - &mut self, - name: &str, - vertex_buffer: BufferId, - index_buffer: Option, - ) { - self.vertex_buffers - .insert(name.to_string(), (vertex_buffer, index_buffer)); + pub fn set_index_buffer(&mut self, index_buffer: BufferId) { + self.index_buffer = Some(index_buffer); } fn create_bind_group(&mut self, descriptor: &BindGroupDescriptor) -> BindGroupStatus { @@ -277,15 +262,6 @@ impl AssetRenderResourceBindings { } } -#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)] -pub struct RenderResourceBindingsId(Uuid); - -impl Default for RenderResourceBindingsId { - fn default() -> Self { - RenderResourceBindingsId(Uuid::new_v4()) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/bevy_render/src/renderer/render_resource_context.rs b/crates/bevy_render/src/renderer/render_resource_context.rs index 8849d51c31ea5..bcaf0155ade80 100644 --- a/crates/bevy_render/src/renderer/render_resource_context.rs +++ b/crates/bevy_render/src/renderer/render_resource_context.rs @@ -38,14 +38,14 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static { &self, handle: HandleUntyped, resource: RenderResourceId, - index: usize, + index: u64, ); fn get_asset_resource_untyped( &self, handle: HandleUntyped, - index: usize, + index: u64, ) -> Option; - fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: usize); + fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: u64); fn create_render_pipeline( &self, pipeline_handle: Handle, @@ -63,29 +63,21 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static { } impl dyn RenderResourceContext { - pub fn set_asset_resource( - &self, - handle: &Handle, - resource: RenderResourceId, - index: usize, - ) where + pub fn set_asset_resource(&self, handle: &Handle, resource: RenderResourceId, index: u64) + where T: Asset, { self.set_asset_resource_untyped(handle.clone_weak_untyped(), resource, index); } - pub fn get_asset_resource( - &self, - handle: &Handle, - index: usize, - ) -> Option + pub fn get_asset_resource(&self, handle: &Handle, index: u64) -> Option where T: Asset, { self.get_asset_resource_untyped(handle.clone_weak_untyped(), index) } - pub fn remove_asset_resource(&self, handle: &Handle, index: usize) + pub fn remove_asset_resource(&self, handle: &Handle, index: u64) where T: Asset, { diff --git a/crates/bevy_render/src/shader/shader_reflect.rs b/crates/bevy_render/src/shader/shader_reflect.rs index 3233818d84001..2a35d0fb21b49 100644 --- a/crates/bevy_render/src/shader/shader_reflect.rs +++ b/crates/bevy_render/src/shader/shader_reflect.rs @@ -7,12 +7,10 @@ use crate::{ texture::{TextureComponentType, TextureViewDimension}, }; use bevy_core::AsBytes; -use bevy_utils::HashSet; use spirv_reflect::{ types::{ ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType, ReflectDimension, - ReflectInterfaceVariable, ReflectShaderStageFlags, ReflectTypeDescription, - ReflectTypeFlags, + ReflectShaderStageFlags, ReflectTypeDescription, ReflectTypeFlags, }, ShaderModule, }; @@ -21,6 +19,7 @@ impl ShaderLayout { pub fn from_spirv(spirv_data: &[u32], bevy_conventions: bool) -> ShaderLayout { match ShaderModule::load_u8_data(spirv_data.as_bytes()) { Ok(ref mut module) => { + // init let entry_point_name = module.get_entry_point_name(); let shader_stage = module.get_shader_stage(); let mut bind_groups = Vec::new(); @@ -29,66 +28,45 @@ impl ShaderLayout { bind_groups.push(bind_group); } + // obtain attribute descriptors from reflection let mut vertex_attribute_descriptors = Vec::new(); for input_variable in module.enumerate_input_variables(None).unwrap() { - let vertex_attribute_descriptor = - reflect_vertex_attribute_descriptor(&input_variable); - if vertex_attribute_descriptor.name == GL_VERTEX_INDEX { + if input_variable.name == GL_VERTEX_INDEX { continue; } - vertex_attribute_descriptors.push(vertex_attribute_descriptor); + // reflect vertex attribute descriptor and record it + vertex_attribute_descriptors.push(VertexAttributeDescriptor { + name: input_variable.name.clone().into(), + format: reflect_vertex_format( + input_variable.type_description.as_ref().unwrap(), + ), + offset: 0, + shader_location: input_variable.location, + }); } vertex_attribute_descriptors .sort_by(|a, b| a.shader_location.cmp(&b.shader_location)); - let mut visited_buffer_descriptors = HashSet::default(); let mut vertex_buffer_descriptors = Vec::new(); - let mut current_descriptor: Option = None; for vertex_attribute_descriptor in vertex_attribute_descriptors.drain(..) { let mut instance = false; + // obtain buffer name and instancing flag let current_buffer_name = { if bevy_conventions { if vertex_attribute_descriptor.name == GL_VERTEX_INDEX { GL_VERTEX_INDEX.to_string() } else { - let parts = vertex_attribute_descriptor - .name - .splitn(3, '_') - .collect::>(); - if parts.len() == 3 { - if parts[0] == "I" { - instance = true; - parts[1].to_string() - } else { - parts[0].to_string() - } - } else if parts.len() == 2 { - parts[0].to_string() - } else { - panic!("Vertex attributes must follow the form BUFFERNAME_PROPERTYNAME. For example: Vertex_Position"); - } + instance = vertex_attribute_descriptor.name.starts_with("I_"); + vertex_attribute_descriptor.name.to_string() } } else { "DefaultVertex".to_string() } }; - if let Some(current) = current_descriptor.as_mut() { - if current.name == current_buffer_name { - current.attributes.push(vertex_attribute_descriptor); - continue; - } else if visited_buffer_descriptors.contains(¤t_buffer_name) { - panic!("Vertex attribute buffer names must be consecutive.") - } - } - - if let Some(current) = current_descriptor.take() { - visited_buffer_descriptors.insert(current.name.to_string()); - vertex_buffer_descriptors.push(current); - } - - current_descriptor = Some(VertexBufferDescriptor { + // create a new buffer descriptor, per attribute! + vertex_buffer_descriptors.push(VertexBufferDescriptor { attributes: vec![vertex_attribute_descriptor], name: current_buffer_name.into(), step_mode: if instance { @@ -97,16 +75,7 @@ impl ShaderLayout { InputStepMode::Vertex }, stride: 0, - }) - } - - if let Some(current) = current_descriptor.take() { - visited_buffer_descriptors.insert(current.name.to_string()); - vertex_buffer_descriptors.push(current); - } - - for vertex_buffer_descriptor in vertex_buffer_descriptors.iter_mut() { - calculate_offsets(vertex_buffer_descriptor); + }); } ShaderLayout { @@ -120,27 +89,6 @@ impl ShaderLayout { } } -fn calculate_offsets(vertex_buffer_descriptor: &mut VertexBufferDescriptor) { - let mut offset = 0; - for attribute in vertex_buffer_descriptor.attributes.iter_mut() { - attribute.offset = offset; - offset += attribute.format.get_size(); - } - - vertex_buffer_descriptor.stride = offset; -} - -fn reflect_vertex_attribute_descriptor( - input_variable: &ReflectInterfaceVariable, -) -> VertexAttributeDescriptor { - VertexAttributeDescriptor { - name: input_variable.name.clone().into(), - format: reflect_vertex_format(input_variable.type_description.as_ref().unwrap()), - offset: 0, - shader_location: input_variable.location, - } -} - fn reflect_bind_group( descriptor_set: &ReflectDescriptorSet, shader_stage: ReflectShaderStageFlags, @@ -352,6 +300,12 @@ mod tests { use super::*; use crate::shader::{Shader, ShaderStage}; + impl VertexBufferDescriptor { + pub fn test_zero_stride(mut self) -> VertexBufferDescriptor { + self.stride = 0; + self + } + } #[test] fn test_reflection() { let vertex_shader = Shader::from_glsl( @@ -382,36 +336,36 @@ mod tests { ShaderLayout { entry_point: "main".into(), vertex_buffer_descriptors: vec![ - VertexBufferDescriptor { - name: "Vertex".into(), - attributes: vec![ - VertexAttributeDescriptor { - name: "Vertex_Position".into(), - format: VertexFormat::Float4, - offset: 0, - shader_location: 0, - }, - VertexAttributeDescriptor { - name: "Vertex_Normal".into(), - format: VertexFormat::Uint4, - offset: 16, - shader_location: 1, - } - ], - step_mode: InputStepMode::Vertex, - stride: 32, - }, - VertexBufferDescriptor { - name: "TestInstancing".into(), - attributes: vec![VertexAttributeDescriptor { + VertexBufferDescriptor::new_from_attribute( + VertexAttributeDescriptor { + name: "Vertex_Position".into(), + format: VertexFormat::Float4, + offset: 0, + shader_location: 0, + }, + InputStepMode::Vertex + ) + .test_zero_stride(), + VertexBufferDescriptor::new_from_attribute( + VertexAttributeDescriptor { + name: "Vertex_Normal".into(), + format: VertexFormat::Uint4, + offset: 0, + shader_location: 1, + }, + InputStepMode::Vertex + ) + .test_zero_stride(), + VertexBufferDescriptor::new_from_attribute( + VertexAttributeDescriptor { name: "I_TestInstancing_Property".into(), format: VertexFormat::Uint4, offset: 0, shader_location: 2, - },], - step_mode: InputStepMode::Instance, - stride: 16, - } + }, + InputStepMode::Instance + ) + .test_zero_stride(), ], bind_groups: vec![ BindGroupDescriptor::new( @@ -443,32 +397,4 @@ mod tests { } ); } - - #[test] - #[should_panic(expected = "Vertex attribute buffer names must be consecutive.")] - fn test_reflection_consecutive_buffer_validation() { - let vertex_shader = Shader::from_glsl( - ShaderStage::Vertex, - r#" - #version 450 - layout(location = 0) in vec4 Vertex_Position; - layout(location = 1) in uvec4 Other_Property; - layout(location = 2) in uvec4 Vertex_Normal; - - layout(location = 0) out vec4 v_Position; - layout(set = 0, binding = 0) uniform Camera { - mat4 ViewProj; - }; - layout(set = 1, binding = 0) uniform texture2D Texture; - - void main() { - v_Position = Vertex_Position; - gl_Position = ViewProj * v_Position; - } - "#, - ) - .get_spirv_shader(None); - - let _layout = vertex_shader.reflect_layout(true).unwrap(); - } } diff --git a/crates/bevy_render/src/texture/texture.rs b/crates/bevy_render/src/texture/texture.rs index 375e5ef2c6882..9f1ff403f9615 100644 --- a/crates/bevy_render/src/texture/texture.rs +++ b/crates/bevy_render/src/texture/texture.rs @@ -9,8 +9,8 @@ use bevy_math::Vec2; use bevy_type_registry::TypeUuid; use bevy_utils::HashSet; -pub const TEXTURE_ASSET_INDEX: usize = 0; -pub const SAMPLER_ASSET_INDEX: usize = 1; +pub const TEXTURE_ASSET_INDEX: u64 = 0; +pub const SAMPLER_ASSET_INDEX: u64 = 1; #[derive(Debug, Clone, TypeUuid)] #[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"] diff --git a/crates/bevy_text/src/draw.rs b/crates/bevy_text/src/draw.rs index e09410b6642b1..3b023f255e94b 100644 --- a/crates/bevy_text/src/draw.rs +++ b/crates/bevy_text/src/draw.rs @@ -6,7 +6,7 @@ use bevy_render::{ color::Color, draw::{Draw, DrawContext, DrawError, Drawable}, mesh, - pipeline::PipelineSpecialization, + pipeline::{PipelineSpecialization, VertexBufferDescriptor}, prelude::Msaa, renderer::{ AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings, @@ -41,6 +41,7 @@ pub struct DrawableText<'a> { pub style: &'a TextStyle, pub text: &'a str, pub msaa: &'a Msaa, + pub font_quad_vertex_descriptor: &'a VertexBufferDescriptor, } impl<'a> Drawable for DrawableText<'a> { @@ -50,16 +51,21 @@ impl<'a> Drawable for DrawableText<'a> { &bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE, &PipelineSpecialization { sample_count: self.msaa.samples, + mesh_attribute_layout: self.font_quad_vertex_descriptor.clone(), ..Default::default() }, )?; let render_resource_context = &**context.render_resource_context; - if let Some(RenderResourceId::Buffer(quad_vertex_buffer)) = render_resource_context - .get_asset_resource(&bevy_sprite::QUAD_HANDLE, mesh::VERTEX_BUFFER_ASSET_INDEX) + + if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_id)) = render_resource_context + .get_asset_resource(&bevy_sprite::QUAD_HANDLE, mesh::VERTEX_ATTRIBUTE_BUFFER_ID) { - draw.set_vertex_buffer(0, quad_vertex_buffer, 0); + draw.set_vertex_buffer(0, vertex_attribute_buffer_id, 0); + } else { + println!("could not find vertex buffer for bevy_sprite::QUAD_HANDLE") } + let mut indices = 0..0; if let Some(RenderResourceId::Buffer(quad_index_buffer)) = render_resource_context .get_asset_resource(&bevy_sprite::QUAD_HANDLE, mesh::INDEX_BUFFER_ASSET_INDEX) @@ -145,6 +151,7 @@ impl<'a> Drawable for DrawableText<'a> { .add_binding(0, transform_buffer) .add_binding(1, sprite_buffer) .finish(); + context.create_bind_group_resource(2, &sprite_bind_group)?; draw.set_bind_group(2, &sprite_bind_group); draw.draw_indexed(indices.clone(), 0, 0..1); diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 47db1442279c8..9759e40d639f8 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -4,11 +4,12 @@ use bevy_ecs::{Changed, Entity, Local, Query, QuerySet, Res, ResMut}; use bevy_math::Size; use bevy_render::{ draw::{Draw, DrawContext, Drawable}, + mesh::Mesh, prelude::Msaa, renderer::{AssetRenderResourceBindings, RenderResourceBindings}, texture::Texture, }; -use bevy_sprite::TextureAtlas; +use bevy_sprite::{TextureAtlas, QUAD_HANDLE}; use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle}; use bevy_transform::prelude::GlobalTransform; @@ -94,10 +95,20 @@ pub fn draw_text_system( msaa: Res, font_atlas_sets: Res>, texture_atlases: Res>, + meshes: Res>, mut render_resource_bindings: ResMut, mut asset_render_resource_bindings: ResMut, mut query: Query<(&mut Draw, &Text, &Node, &GlobalTransform)>, ) { + let font_quad_vertex_descriptor = { + let font_quad = meshes.get(&QUAD_HANDLE).unwrap(); + font_quad + .attribute_buffer_descriptor_reference + .as_ref() + .unwrap() + .clone() + }; + for (mut draw, text, node, global_transform) in query.iter_mut() { if let Some(font) = fonts.get(&text.font) { let position = global_transform.translation - (node.size / 2.0).extend(0.0); @@ -112,6 +123,7 @@ pub fn draw_text_system( style: &text.style, text: &text.value, container_size: node.size, + font_quad_vertex_descriptor: &font_quad_vertex_descriptor, }; drawable_text.draw(&mut draw, &mut draw_context).unwrap(); } diff --git a/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs b/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs index 554f73b80c070..ab5048da4a033 100644 --- a/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs +++ b/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs @@ -311,7 +311,7 @@ impl RenderResourceContext for WgpuRenderResourceContext { &self, handle: HandleUntyped, render_resource: RenderResourceId, - index: usize, + index: u64, ) { let mut asset_resources = self.resources.asset_resources.write(); asset_resources.insert((handle, index), render_resource); @@ -320,13 +320,13 @@ impl RenderResourceContext for WgpuRenderResourceContext { fn get_asset_resource_untyped( &self, handle: HandleUntyped, - index: usize, + index: u64, ) -> Option { let asset_resources = self.resources.asset_resources.read(); asset_resources.get(&(handle, index)).cloned() } - fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: usize) { + fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: u64) { let mut asset_resources = self.resources.asset_resources.write(); asset_resources.remove(&(handle, index)); } diff --git a/crates/bevy_wgpu/src/wgpu_resources.rs b/crates/bevy_wgpu/src/wgpu_resources.rs index 5e2bedb275152..f36f3644882a3 100644 --- a/crates/bevy_wgpu/src/wgpu_resources.rs +++ b/crates/bevy_wgpu/src/wgpu_resources.rs @@ -84,7 +84,7 @@ pub struct WgpuResources { pub render_pipelines: Arc, wgpu::RenderPipeline>>>, pub bind_groups: Arc>>, pub bind_group_layouts: Arc>>, - pub asset_resources: Arc>>, + pub asset_resources: Arc>>, } impl WgpuResources { diff --git a/crates/bevy_wgpu/src/wgpu_type_converter.rs b/crates/bevy_wgpu/src/wgpu_type_converter.rs index 3769024c0be0a..8ffc846b63f44 100644 --- a/crates/bevy_wgpu/src/wgpu_type_converter.rs +++ b/crates/bevy_wgpu/src/wgpu_type_converter.rs @@ -103,6 +103,7 @@ impl WgpuFrom<&VertexBufferDescriptor> for OwnedWgpuVertexBufferDescriptor { .iter() .map(|a| a.wgpu_into()) .collect::>(); + OwnedWgpuVertexBufferDescriptor { step_mode: val.step_mode.wgpu_into(), stride: val.stride,