Mesh overhaul with custom vertex attributes
This commit is contained in:
parent
ad940fbf6e
commit
4645da30c8
17
CHANGELOG.md
17
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]
|
||||
|
||||
@ -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::<Option<ignore>>()?.is_some() {
|
||||
vertex_attributes.ignore = true;
|
||||
return Ok(());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.expect("invalid 'vertex' attribute format");
|
||||
|
||||
vertex_attributes
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(&Field, VertexAttributes)>>();
|
||||
|
||||
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::<Vec<VertexAttributeDescriptor>>()
|
||||
}).flatten().collect::<Vec<VertexAttributeDescriptor>>();
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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<Shader>>,
|
||||
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
|
||||
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
|
||||
pub vertex_buffer_descriptors: Res<'a, VertexBufferDescriptors>,
|
||||
pub shared_buffers: Res<'a, SharedBuffers>,
|
||||
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
|
||||
}
|
||||
@ -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::<Assets<Shader>>();
|
||||
resources.borrow_mut::<PipelineCompiler>();
|
||||
resources.borrow::<Box<dyn RenderResourceContext>>();
|
||||
resources.borrow::<VertexBufferDescriptors>();
|
||||
resources.borrow::<SharedBuffers>();
|
||||
}
|
||||
|
||||
@ -175,7 +172,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
||||
resources.release_mut::<Assets<Shader>>();
|
||||
resources.release_mut::<PipelineCompiler>();
|
||||
resources.release::<Box<dyn RenderResourceContext>>();
|
||||
resources.release::<VertexBufferDescriptors>();
|
||||
resources.release::<SharedBuffers>();
|
||||
}
|
||||
|
||||
@ -205,9 +201,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
||||
render_resource_context: Res::new(
|
||||
resources.get_unsafe_ref::<Box<dyn RenderResourceContext>>(ResourceIndex::Global),
|
||||
),
|
||||
vertex_buffer_descriptors: Res::new(
|
||||
resources.get_unsafe_ref::<VertexBufferDescriptors>(ResourceIndex::Global),
|
||||
),
|
||||
shared_buffers: Res::new(
|
||||
resources.get_unsafe_ref::<SharedBuffers>(ResourceIndex::Global),
|
||||
),
|
||||
@ -221,7 +214,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
||||
access.add_write(TypeId::of::<Assets<Shader>>());
|
||||
access.add_write(TypeId::of::<PipelineCompiler>());
|
||||
access.add_read(TypeId::of::<Box<dyn RenderResourceContext>>());
|
||||
access.add_read(TypeId::of::<VertexBufferDescriptors>());
|
||||
access.add_read(TypeId::of::<SharedBuffers>());
|
||||
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()
|
||||
{
|
||||
// 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((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 {
|
||||
if let Some(index_buffer) = bindings.index_buffer {
|
||||
draw.set_index_buffer(index_buffer, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::<RenderGraph>()
|
||||
.init_resource::<PipelineCompiler>()
|
||||
.init_resource::<RenderResourceBindings>()
|
||||
.init_resource::<VertexBufferDescriptors>()
|
||||
.init_resource::<TextureResourceSystemState>()
|
||||
.init_resource::<AssetRenderResourceBindings>()
|
||||
.init_resource::<ActiveCameras>()
|
||||
|
||||
@ -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<f32>),
|
||||
@ -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<Vec<f32>> for VertexAttributeValues {
|
||||
fn from(vec: Vec<f32>) -> Self {
|
||||
VertexAttributeValues::Float(vec)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn normal(normals: Vec<[f32; 3]>) -> Self {
|
||||
VertexAttribute {
|
||||
name: Self::NORMAL.into(),
|
||||
values: VertexAttributeValues::Float3(normals),
|
||||
impl From<Vec<[f32; 2]>> 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<Vec<[f32; 3]>> 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<Vec<[f32; 4]>> for VertexAttributeValues {
|
||||
fn from(vec: Vec<[f32; 4]>) -> Self {
|
||||
VertexAttributeValues::Float4(vec)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -112,61 +89,34 @@ pub enum Indices {
|
||||
U16(Vec<u16>),
|
||||
U32(Vec<u32>),
|
||||
}
|
||||
// TODO: allow values to be unloaded after been submitting to the GPU to conserve memory
|
||||
pub type VertexAttributesHashMap = HashMap<Cow<'static, str>, VertexAttributeValues>;
|
||||
|
||||
#[derive(Debug, TypeUuid)]
|
||||
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
|
||||
pub struct Mesh {
|
||||
pub primitive_topology: PrimitiveTopology,
|
||||
pub attributes: Vec<VertexAttribute>,
|
||||
/// `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<Indices>,
|
||||
/// 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<VertexBufferDescriptor>,
|
||||
}
|
||||
|
||||
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<Vec<u8>, 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<Vec<u8>> {
|
||||
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_resource_save(
|
||||
render_resource_context: &dyn RenderResourceContext,
|
||||
handle: &Handle<Mesh>,
|
||||
index: u64,
|
||||
) {
|
||||
if let Some(RenderResourceId::Buffer(buffer)) =
|
||||
render_resource_context.get_asset_resource(&handle, index)
|
||||
{
|
||||
render_resource_context.remove_buffer(buffer);
|
||||
render_resource_context.remove_asset_resource(handle, index);
|
||||
}
|
||||
}
|
||||
fn remove_current_mesh_resources(
|
||||
render_resource_context: &dyn RenderResourceContext,
|
||||
handle: &Handle<Mesh>,
|
||||
) {
|
||||
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.remove_buffer(buffer);
|
||||
render_resource_context.remove_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX);
|
||||
}
|
||||
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<AssetEvent<Mesh>>,
|
||||
vertex_buffer_descriptor: Option<&'static VertexBufferDescriptor>,
|
||||
}
|
||||
|
||||
pub fn mesh_resource_provider_system(
|
||||
mut state: Local<MeshResourceProviderState>,
|
||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||
meshes: Res<Assets<Mesh>>,
|
||||
mut vertex_buffer_descriptors: ResMut<VertexBufferDescriptors>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mesh_events: Res<Events<AssetEvent<Mesh>>>,
|
||||
mut query: Query<(&Handle<Mesh>, &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::<Handle<Mesh>>::default();
|
||||
let mut changed_meshes = bevy_utils::HashSet::<Handle<Mesh>>::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,
|
||||
);
|
||||
render_resource_context.set_asset_resource(
|
||||
changed_mesh_handle,
|
||||
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(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)
|
||||
if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
|
||||
render_resource_context.get_asset_resource(handle, INDEX_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
|
||||
// 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;
|
||||
pub fn attributes_count_vertices(attributes: &VertexAttributesHashMap) -> Option<u32> {
|
||||
let mut vertex_count: Option<u32> = 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);
|
||||
}
|
||||
vertex_count = Some(attribute_len as u32);
|
||||
}
|
||||
vertex_count
|
||||
}
|
||||
pub fn attributes_to_vertex_buffer_data(
|
||||
attributes: &VertexAttributesHashMap,
|
||||
vertex_count: u32,
|
||||
) -> (Vec<u8>, 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,
|
||||
},
|
||||
attribute_data.1.get_bytes(),
|
||||
));
|
||||
accumulated_offset += vertex_format.get_size();
|
||||
}
|
||||
let mut attributes_interleaved_buffer = Vec::<u8>::default();
|
||||
|
||||
#[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);
|
||||
// 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 mesh = Mesh {
|
||||
primitive_topology: PrimitiveTopology::TriangleStrip,
|
||||
attributes: vec![
|
||||
VertexAttribute::position(positions),
|
||||
VertexAttribute::normal(normals),
|
||||
VertexAttribute::uv(uvs),
|
||||
],
|
||||
indices: None,
|
||||
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(),
|
||||
};
|
||||
|
||||
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.],
|
||||
},
|
||||
];
|
||||
|
||||
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"
|
||||
);
|
||||
}
|
||||
(
|
||||
attributes_interleaved_buffer,
|
||||
vertex_buffer_descriptor_reference,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
mod mesh;
|
||||
mod vertex;
|
||||
|
||||
pub use mesh::*;
|
||||
pub use vertex::*;
|
||||
|
||||
@ -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 {}
|
||||
@ -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<Shader>,
|
||||
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
|
||||
|
||||
@ -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<DynamicBinding>,
|
||||
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<PipelineDescriptor>,
|
||||
shaders: &mut Assets<Shader>,
|
||||
source_pipeline: &Handle<PipelineDescriptor>,
|
||||
vertex_buffer_descriptors: &VertexBufferDescriptors,
|
||||
pipeline_specialization: &PipelineSpecialization,
|
||||
) -> Handle<PipelineDescriptor> {
|
||||
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::<VertexBufferDescriptor>::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;
|
||||
|
||||
@ -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)]
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use super::VertexFormat;
|
||||
use bevy_utils::HashMap;
|
||||
use std::borrow::Cow;
|
||||
use bevy_property::Property;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
pub use bevy_derive::AsVertexBufferDescriptor;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[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<VertexAttributeDescriptor>,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
self.stride = descriptor.stride;
|
||||
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()],
|
||||
}
|
||||
}
|
||||
|
||||
#[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<String, VertexBufferDescriptor>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -15,7 +15,7 @@ use std::{ops::Range, sync::Arc};
|
||||
pub struct HeadlessRenderResourceContext {
|
||||
buffer_info: Arc<RwLock<HashMap<BufferId, BufferInfo>>>,
|
||||
texture_descriptors: Arc<RwLock<HashMap<TextureId, TextureDescriptor>>>,
|
||||
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, usize), RenderResourceId>>>,
|
||||
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, u64), RenderResourceId>>>,
|
||||
}
|
||||
|
||||
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<RenderResourceId> {
|
||||
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: &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));
|
||||
}
|
||||
|
||||
|
||||
@ -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<String, RenderResourceBinding>,
|
||||
// TODO: remove this
|
||||
vertex_buffers: HashMap<String, (BufferId, Option<BufferId>)>,
|
||||
/// A Buffer that contains all attributes a mesh has defined
|
||||
pub vertex_attribute_buffer: Option<BufferId>,
|
||||
/// 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<BufferId>,
|
||||
pub index_buffer: Option<BufferId>,
|
||||
bind_groups: HashMap<BindGroupId, BindGroup>,
|
||||
bind_group_descriptors: HashMap<BindGroupDescriptorId, Option<BindGroupId>>,
|
||||
dirty_bind_groups: HashSet<BindGroupId>,
|
||||
@ -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<BufferId>)> {
|
||||
self.vertex_buffers.get(name).cloned()
|
||||
}
|
||||
|
||||
pub fn set_vertex_buffer(
|
||||
&mut self,
|
||||
name: &str,
|
||||
vertex_buffer: BufferId,
|
||||
index_buffer: Option<BufferId>,
|
||||
) {
|
||||
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::*;
|
||||
|
||||
@ -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<RenderResourceId>;
|
||||
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<PipelineDescriptor>,
|
||||
@ -63,29 +63,21 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
|
||||
}
|
||||
|
||||
impl dyn RenderResourceContext {
|
||||
pub fn set_asset_resource<T>(
|
||||
&self,
|
||||
handle: &Handle<T>,
|
||||
resource: RenderResourceId,
|
||||
index: usize,
|
||||
) where
|
||||
pub fn set_asset_resource<T>(&self, handle: &Handle<T>, resource: RenderResourceId, index: u64)
|
||||
where
|
||||
T: Asset,
|
||||
{
|
||||
self.set_asset_resource_untyped(handle.clone_weak_untyped(), resource, index);
|
||||
}
|
||||
|
||||
pub fn get_asset_resource<T>(
|
||||
&self,
|
||||
handle: &Handle<T>,
|
||||
index: usize,
|
||||
) -> Option<RenderResourceId>
|
||||
pub fn get_asset_resource<T>(&self, handle: &Handle<T>, index: u64) -> Option<RenderResourceId>
|
||||
where
|
||||
T: Asset,
|
||||
{
|
||||
self.get_asset_resource_untyped(handle.clone_weak_untyped(), index)
|
||||
}
|
||||
|
||||
pub fn remove_asset_resource<T>(&self, handle: &Handle<T>, index: usize)
|
||||
pub fn remove_asset_resource<T>(&self, handle: &Handle<T>, index: u64)
|
||||
where
|
||||
T: Asset,
|
||||
{
|
||||
|
||||
@ -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<VertexBufferDescriptor> = 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::<Vec<&str>>();
|
||||
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![
|
||||
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: 16,
|
||||
offset: 0,
|
||||
shader_location: 1,
|
||||
}
|
||||
],
|
||||
step_mode: InputStepMode::Vertex,
|
||||
stride: 32,
|
||||
},
|
||||
VertexBufferDescriptor {
|
||||
name: "TestInstancing".into(),
|
||||
attributes: vec![VertexAttributeDescriptor {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"]
|
||||
|
||||
@ -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)
|
||||
@ -140,6 +146,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);
|
||||
|
||||
@ -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<Msaa>,
|
||||
font_atlas_sets: Res<Assets<FontAtlasSet>>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
meshes: Res<Assets<Mesh>>,
|
||||
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
||||
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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<RenderResourceId> {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ pub struct WgpuResources {
|
||||
pub render_pipelines: Arc<RwLock<HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>>,
|
||||
pub bind_groups: Arc<RwLock<HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>>,
|
||||
pub bind_group_layouts: Arc<RwLock<HashMap<BindGroupDescriptorId, wgpu::BindGroupLayout>>>,
|
||||
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, usize), RenderResourceId>>>,
|
||||
pub asset_resources: Arc<RwLock<HashMap<(HandleUntyped, u64), RenderResourceId>>>,
|
||||
}
|
||||
|
||||
impl WgpuResources {
|
||||
|
||||
@ -103,6 +103,7 @@ impl WgpuFrom<&VertexBufferDescriptor> for OwnedWgpuVertexBufferDescriptor {
|
||||
.iter()
|
||||
.map(|a| a.wgpu_into())
|
||||
.collect::<Vec<wgpu::VertexAttributeDescriptor>>();
|
||||
|
||||
OwnedWgpuVertexBufferDescriptor {
|
||||
step_mode: val.step_mode.wgpu_into(),
|
||||
stride: val.stride,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user