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
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
### 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]
|
- [Introduce Mouse capture API][679]
|
||||||
- [`bevy_input::touch`: implement touch input][696]
|
- [`bevy_input::touch`: implement touch input][696]
|
||||||
- [D-pad support on MacOS][653]
|
- [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.
|
`Color::rgb` and `Color::rgba` will be converted to linear sRGB under-the-hood.
|
||||||
- This allows drop-in use of colors from most applications.
|
- 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)
|
- 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:
|
- 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.
|
`.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]
|
- 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
|
### 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 update bind group ids when setting dynamic bindings][560]
|
||||||
- [Properly exit the app on AppExit event][610]
|
- [Properly exit the app on AppExit event][610]
|
||||||
- [Fix FloatOrd hash being different for different NaN values][618]
|
- [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;
|
extern crate proc_macro;
|
||||||
|
|
||||||
mod app_plugin;
|
mod app_plugin;
|
||||||
mod as_vertex_buffer_descriptor;
|
|
||||||
mod bytes;
|
mod bytes;
|
||||||
mod modules;
|
mod modules;
|
||||||
mod render_resource;
|
mod render_resource;
|
||||||
@ -45,12 +44,6 @@ pub fn derive_shader_defs(input: TokenStream) -> TokenStream {
|
|||||||
shader_defs::derive_shader_defs(input)
|
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.
|
/// Generates a dynamic plugin entry point function for the given `Plugin` type.
|
||||||
#[proc_macro_derive(DynamicPlugin)]
|
#[proc_macro_derive(DynamicPlugin)]
|
||||||
pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
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_math::Mat4;
|
||||||
use bevy_pbr::prelude::{PbrComponents, StandardMaterial};
|
use bevy_pbr::prelude::{PbrComponents, StandardMaterial};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
mesh::{Indices, Mesh, VertexAttribute},
|
mesh::{Indices, Mesh, VertexAttributeValues},
|
||||||
pipeline::PrimitiveTopology,
|
pipeline::PrimitiveTopology,
|
||||||
prelude::{Color, Texture},
|
prelude::{Color, Texture},
|
||||||
texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat},
|
texture::{AddressMode, FilterMode, SamplerDescriptor, TextureFormat},
|
||||||
@ -20,7 +20,7 @@ use gltf::{
|
|||||||
Primitive,
|
Primitive,
|
||||||
};
|
};
|
||||||
use image::{GenericImageView, ImageFormat};
|
use image::{GenericImageView, ImageFormat};
|
||||||
use std::path::Path;
|
use std::{borrow::Cow, path::Path};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// An error that occurs when loading a GLTF file
|
/// 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
|
if let Some(vertex_attribute) = reader
|
||||||
.read_positions()
|
.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
|
if let Some(vertex_attribute) = reader
|
||||||
.read_normals()
|
.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
|
if let Some(vertex_attribute) = reader
|
||||||
.read_tex_coords(0)
|
.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() {
|
if let Some(indices) = reader.read_indices() {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
pipeline::{
|
pipeline::{
|
||||||
PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization,
|
PipelineCompiler, PipelineDescriptor, PipelineLayout, PipelineSpecialization,
|
||||||
VertexBufferDescriptors,
|
VERTEX_FALLBACK_LAYOUT_NAME,
|
||||||
},
|
},
|
||||||
renderer::{
|
renderer::{
|
||||||
BindGroup, BindGroupId, BufferId, BufferUsage, RenderResource, RenderResourceBinding,
|
BindGroup, BindGroupId, BufferId, BufferUsage, RenderResource, RenderResourceBinding,
|
||||||
@ -131,7 +131,6 @@ pub struct DrawContext<'a> {
|
|||||||
pub shaders: ResMut<'a, Assets<Shader>>,
|
pub shaders: ResMut<'a, Assets<Shader>>,
|
||||||
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
|
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
|
||||||
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
|
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
|
||||||
pub vertex_buffer_descriptors: Res<'a, VertexBufferDescriptors>,
|
|
||||||
pub shared_buffers: Res<'a, SharedBuffers>,
|
pub shared_buffers: Res<'a, SharedBuffers>,
|
||||||
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
|
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
|
||||||
}
|
}
|
||||||
@ -143,7 +142,6 @@ impl<'a> UnsafeClone for DrawContext<'a> {
|
|||||||
shaders: self.shaders.unsafe_clone(),
|
shaders: self.shaders.unsafe_clone(),
|
||||||
pipeline_compiler: self.pipeline_compiler.unsafe_clone(),
|
pipeline_compiler: self.pipeline_compiler.unsafe_clone(),
|
||||||
render_resource_context: self.render_resource_context.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(),
|
shared_buffers: self.shared_buffers.unsafe_clone(),
|
||||||
current_pipeline: self.current_pipeline.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::<Assets<Shader>>();
|
||||||
resources.borrow_mut::<PipelineCompiler>();
|
resources.borrow_mut::<PipelineCompiler>();
|
||||||
resources.borrow::<Box<dyn RenderResourceContext>>();
|
resources.borrow::<Box<dyn RenderResourceContext>>();
|
||||||
resources.borrow::<VertexBufferDescriptors>();
|
|
||||||
resources.borrow::<SharedBuffers>();
|
resources.borrow::<SharedBuffers>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +172,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
|||||||
resources.release_mut::<Assets<Shader>>();
|
resources.release_mut::<Assets<Shader>>();
|
||||||
resources.release_mut::<PipelineCompiler>();
|
resources.release_mut::<PipelineCompiler>();
|
||||||
resources.release::<Box<dyn RenderResourceContext>>();
|
resources.release::<Box<dyn RenderResourceContext>>();
|
||||||
resources.release::<VertexBufferDescriptors>();
|
|
||||||
resources.release::<SharedBuffers>();
|
resources.release::<SharedBuffers>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,9 +201,6 @@ impl<'a> FetchResource<'a> for FetchDrawContext {
|
|||||||
render_resource_context: Res::new(
|
render_resource_context: Res::new(
|
||||||
resources.get_unsafe_ref::<Box<dyn RenderResourceContext>>(ResourceIndex::Global),
|
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(
|
shared_buffers: Res::new(
|
||||||
resources.get_unsafe_ref::<SharedBuffers>(ResourceIndex::Global),
|
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::<Assets<Shader>>());
|
||||||
access.add_write(TypeId::of::<PipelineCompiler>());
|
access.add_write(TypeId::of::<PipelineCompiler>());
|
||||||
access.add_read(TypeId::of::<Box<dyn RenderResourceContext>>());
|
access.add_read(TypeId::of::<Box<dyn RenderResourceContext>>());
|
||||||
access.add_read(TypeId::of::<VertexBufferDescriptors>());
|
|
||||||
access.add_read(TypeId::of::<SharedBuffers>());
|
access.add_read(TypeId::of::<SharedBuffers>());
|
||||||
access
|
access
|
||||||
}
|
}
|
||||||
@ -262,7 +254,6 @@ impl<'a> DrawContext<'a> {
|
|||||||
&mut self.pipelines,
|
&mut self.pipelines,
|
||||||
&mut self.shaders,
|
&mut self.shaders,
|
||||||
pipeline_handle,
|
pipeline_handle,
|
||||||
&self.vertex_buffer_descriptors,
|
|
||||||
specialization,
|
specialization,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -358,18 +349,21 @@ impl<'a> DrawContext<'a> {
|
|||||||
let layout = pipeline_descriptor
|
let layout = pipeline_descriptor
|
||||||
.get_layout()
|
.get_layout()
|
||||||
.ok_or(DrawError::PipelineHasNoLayout)?;
|
.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() {
|
for bindings in render_resource_bindings.iter() {
|
||||||
if let Some((vertex_buffer, index_buffer)) =
|
if let Some(index_buffer) = bindings.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);
|
draw.set_index_buffer(index_buffer, 0);
|
||||||
}
|
}
|
||||||
|
if let Some(main_vertex_buffer) = bindings.vertex_attribute_buffer {
|
||||||
break;
|
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::{
|
use pipeline::{
|
||||||
DynamicBinding, IndexFormat, PipelineCompiler, PipelineDescriptor, PipelineSpecialization,
|
DynamicBinding, IndexFormat, PipelineCompiler, PipelineDescriptor, PipelineSpecialization,
|
||||||
PrimitiveTopology, ShaderSpecialization, VertexBufferDescriptors,
|
PrimitiveTopology, ShaderSpecialization,
|
||||||
};
|
};
|
||||||
use render_graph::{
|
use render_graph::{
|
||||||
base::{self, BaseRenderGraphBuilder, BaseRenderGraphConfig},
|
base::{self, BaseRenderGraphBuilder, BaseRenderGraphConfig},
|
||||||
@ -119,7 +119,6 @@ impl Plugin for RenderPlugin {
|
|||||||
.init_resource::<RenderGraph>()
|
.init_resource::<RenderGraph>()
|
||||||
.init_resource::<PipelineCompiler>()
|
.init_resource::<PipelineCompiler>()
|
||||||
.init_resource::<RenderResourceBindings>()
|
.init_resource::<RenderResourceBindings>()
|
||||||
.init_resource::<VertexBufferDescriptors>()
|
|
||||||
.init_resource::<TextureResourceSystemState>()
|
.init_resource::<TextureResourceSystemState>()
|
||||||
.init_resource::<AssetRenderResourceBindings>()
|
.init_resource::<AssetRenderResourceBindings>()
|
||||||
.init_resource::<ActiveCameras>()
|
.init_resource::<ActiveCameras>()
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
use super::Vertex;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pipeline::{
|
pipeline::{PrimitiveTopology, RenderPipelines, VertexFormat},
|
||||||
AsVertexBufferDescriptor, PrimitiveTopology, RenderPipelines, VertexBufferDescriptor,
|
|
||||||
VertexBufferDescriptors, VertexFormat,
|
|
||||||
},
|
|
||||||
renderer::{BufferInfo, BufferUsage, RenderResourceContext, RenderResourceId},
|
renderer::{BufferInfo, BufferUsage, RenderResourceContext, RenderResourceId},
|
||||||
};
|
};
|
||||||
use bevy_app::prelude::{EventReader, Events};
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
@ -12,12 +8,14 @@ use bevy_core::AsBytes;
|
|||||||
use bevy_ecs::{Local, Query, Res, ResMut};
|
use bevy_ecs::{Local, Query, Res, ResMut};
|
||||||
use bevy_math::*;
|
use bevy_math::*;
|
||||||
use bevy_type_registry::TypeUuid;
|
use bevy_type_registry::TypeUuid;
|
||||||
use bevy_utils::HashSet;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
pub const VERTEX_BUFFER_ASSET_INDEX: usize = 0;
|
use crate::pipeline::{InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor};
|
||||||
pub const INDEX_BUFFER_ASSET_INDEX: usize = 1;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum VertexAttributeValues {
|
pub enum VertexAttributeValues {
|
||||||
Float(Vec<f32>),
|
Float(Vec<f32>),
|
||||||
@ -62,49 +60,28 @@ impl From<&VertexAttributeValues> for VertexFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl From<Vec<f32>> for VertexAttributeValues {
|
||||||
pub struct VertexAttribute {
|
fn from(vec: Vec<f32>) -> Self {
|
||||||
pub name: Cow<'static, str>,
|
VertexAttributeValues::Float(vec)
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normal(normals: Vec<[f32; 3]>) -> Self {
|
impl From<Vec<[f32; 2]>> for VertexAttributeValues {
|
||||||
VertexAttribute {
|
fn from(vec: Vec<[f32; 2]>) -> Self {
|
||||||
name: Self::NORMAL.into(),
|
VertexAttributeValues::Float2(vec)
|
||||||
values: VertexAttributeValues::Float3(normals),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uv(uvs: Vec<[f32; 2]>) -> Self {
|
impl From<Vec<[f32; 3]>> for VertexAttributeValues {
|
||||||
VertexAttribute {
|
fn from(vec: Vec<[f32; 3]>) -> Self {
|
||||||
name: Self::UV.into(),
|
VertexAttributeValues::Float3(vec)
|
||||||
values: VertexAttributeValues::Float2(uvs),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
impl From<Vec<[f32; 4]>> for VertexAttributeValues {
|
||||||
pub enum MeshToVertexBufferError {
|
fn from(vec: Vec<[f32; 4]>) -> Self {
|
||||||
#[error("VertexBufferDescriptor requires a VertexBufferAttribute this Mesh does not contain.")]
|
VertexAttributeValues::Float4(vec)
|
||||||
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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -112,61 +89,34 @@ pub enum Indices {
|
|||||||
U16(Vec<u16>),
|
U16(Vec<u16>),
|
||||||
U32(Vec<u32>),
|
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)]
|
#[derive(Debug, TypeUuid)]
|
||||||
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
|
#[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
pub primitive_topology: PrimitiveTopology,
|
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>,
|
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 {
|
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 {
|
pub fn new(primitive_topology: PrimitiveTopology) -> Self {
|
||||||
Mesh {
|
Mesh {
|
||||||
primitive_topology,
|
primitive_topology,
|
||||||
attributes: Vec::new(),
|
attributes: Default::default(),
|
||||||
indices: None,
|
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>> {
|
pub fn get_index_buffer_bytes(&self) -> Option<Vec<u8>> {
|
||||||
self.indices.as_ref().map(|indices| match &indices {
|
self.indices.as_ref().map(|indices| match &indices {
|
||||||
Indices::U16(indices) => indices.as_slice().as_bytes().to_vec(),
|
Indices::U16(indices) => indices.as_slice().as_bytes().to_vec(),
|
||||||
@ -177,10 +127,11 @@ impl Mesh {
|
|||||||
|
|
||||||
/// Generation for some primitive shape meshes.
|
/// Generation for some primitive shape meshes.
|
||||||
pub mod shape {
|
pub mod shape {
|
||||||
use super::{Indices, Mesh, VertexAttribute};
|
use super::{Indices, Mesh};
|
||||||
use crate::pipeline::PrimitiveTopology;
|
use crate::pipeline::PrimitiveTopology;
|
||||||
use bevy_math::*;
|
use bevy_math::*;
|
||||||
use hexasphere::Hexasphere;
|
use hexasphere::Hexasphere;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// A cube.
|
/// A cube.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -249,15 +200,15 @@ pub mod shape {
|
|||||||
20, 21, 22, 22, 23, 20, // back
|
20, 21, 22, 22, 23, 20, // back
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Mesh {
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
primitive_topology: PrimitiveTopology::TriangleList,
|
mesh.attributes
|
||||||
attributes: vec![
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into());
|
||||||
VertexAttribute::position(positions),
|
mesh.attributes
|
||||||
VertexAttribute::normal(normals),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into());
|
||||||
VertexAttribute::uv(uvs),
|
mesh.attributes
|
||||||
],
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into());
|
||||||
indices: Some(indices),
|
mesh.indices = Some(indices);
|
||||||
}
|
mesh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,24 +290,24 @@ pub mod shape {
|
|||||||
|
|
||||||
let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]);
|
let indices = Indices::U32(vec![0, 2, 1, 0, 3, 2]);
|
||||||
|
|
||||||
let mut positions = Vec::new();
|
let mut positions = Vec::<[f32; 3]>::new();
|
||||||
let mut normals = Vec::new();
|
let mut normals = Vec::<[f32; 3]>::new();
|
||||||
let mut uvs = Vec::new();
|
let mut uvs = Vec::<[f32; 2]>::new();
|
||||||
for (position, normal, uv) in vertices.iter() {
|
for (position, normal, uv) in vertices.iter() {
|
||||||
positions.push(*position);
|
positions.push(*position);
|
||||||
normals.push(*normal);
|
normals.push(*normal);
|
||||||
uvs.push(*uv);
|
uvs.push(*uv);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh {
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
primitive_topology: PrimitiveTopology::TriangleList,
|
mesh.indices = Some(indices);
|
||||||
attributes: vec![
|
mesh.attributes
|
||||||
VertexAttribute::position(positions),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into());
|
||||||
VertexAttribute::normal(normals),
|
mesh.attributes
|
||||||
VertexAttribute::uv(uvs),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into());
|
||||||
],
|
mesh.attributes
|
||||||
indices: Some(indices),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into());
|
||||||
}
|
mesh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,15 +340,15 @@ pub mod shape {
|
|||||||
uvs.push(*uv);
|
uvs.push(*uv);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh {
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
primitive_topology: PrimitiveTopology::TriangleList,
|
mesh.indices = Some(indices);
|
||||||
attributes: vec![
|
mesh.attributes
|
||||||
VertexAttribute::position(positions),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), positions.into());
|
||||||
VertexAttribute::normal(normals),
|
mesh.attributes
|
||||||
VertexAttribute::uv(uvs),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into());
|
||||||
],
|
mesh.attributes
|
||||||
indices: Some(indices),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_UV_0), uvs.into());
|
||||||
}
|
mesh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,62 +414,53 @@ pub mod shape {
|
|||||||
|
|
||||||
let indices = Indices::U32(indices);
|
let indices = Indices::U32(indices);
|
||||||
|
|
||||||
Mesh {
|
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
primitive_topology: PrimitiveTopology::TriangleList,
|
mesh.indices = Some(indices);
|
||||||
attributes: vec![
|
mesh.attributes
|
||||||
VertexAttribute::position(points),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_POSITION), points.into());
|
||||||
VertexAttribute::normal(normals),
|
mesh.attributes
|
||||||
VertexAttribute::uv(uvs),
|
.insert(Cow::Borrowed(Mesh::ATTRIBUTE_NORMAL), normals.into());
|
||||||
],
|
mesh.attributes
|
||||||
indices: Some(indices),
|
.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(
|
fn remove_current_mesh_resources(
|
||||||
render_resource_context: &dyn RenderResourceContext,
|
render_resource_context: &dyn RenderResourceContext,
|
||||||
handle: &Handle<Mesh>,
|
handle: &Handle<Mesh>,
|
||||||
) {
|
) {
|
||||||
if let Some(RenderResourceId::Buffer(buffer)) =
|
remove_resource_save(render_resource_context, handle, VERTEX_ATTRIBUTE_BUFFER_ID);
|
||||||
render_resource_context.get_asset_resource(&handle, VERTEX_BUFFER_ASSET_INDEX)
|
remove_resource_save(render_resource_context, handle, VERTEX_FALLBACK_BUFFER_ID);
|
||||||
{
|
remove_resource_save(render_resource_context, handle, INDEX_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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MeshResourceProviderState {
|
pub struct MeshResourceProviderState {
|
||||||
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
|
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
|
||||||
vertex_buffer_descriptor: Option<&'static VertexBufferDescriptor>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mesh_resource_provider_system(
|
pub fn mesh_resource_provider_system(
|
||||||
mut state: Local<MeshResourceProviderState>,
|
mut state: Local<MeshResourceProviderState>,
|
||||||
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
render_resource_context: Res<Box<dyn RenderResourceContext>>,
|
||||||
meshes: Res<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut vertex_buffer_descriptors: ResMut<VertexBufferDescriptors>,
|
|
||||||
mesh_events: Res<Events<AssetEvent<Mesh>>>,
|
mesh_events: Res<Events<AssetEvent<Mesh>>>,
|
||||||
mut query: Query<(&Handle<Mesh>, &mut RenderPipelines)>,
|
mut query: Query<(&Handle<Mesh>, &mut RenderPipelines)>,
|
||||||
) {
|
) {
|
||||||
let vertex_buffer_descriptor = match state.vertex_buffer_descriptor {
|
let mut changed_meshes = bevy_utils::HashSet::<Handle<Mesh>>::default();
|
||||||
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 render_resource_context = &**render_resource_context;
|
let render_resource_context = &**render_resource_context;
|
||||||
for event in state.mesh_event_reader.iter(&mesh_events) {
|
for event in state.mesh_event_reader.iter(&mesh_events) {
|
||||||
match event {
|
match event {
|
||||||
@ -538,126 +480,151 @@ pub fn mesh_resource_provider_system(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update changed mesh data
|
||||||
for changed_mesh_handle in changed_meshes.iter() {
|
for changed_mesh_handle in changed_meshes.iter() {
|
||||||
if let Some(mesh) = meshes.get(changed_mesh_handle) {
|
if let Some(mesh) = meshes.get_mut(changed_mesh_handle) {
|
||||||
let vertex_bytes = mesh
|
// TODO: check for individual buffer changes in non-interleaved mode
|
||||||
.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();
|
|
||||||
let index_buffer = render_resource_context.create_buffer_with_data(
|
let index_buffer = render_resource_context.create_buffer_with_data(
|
||||||
BufferInfo {
|
BufferInfo {
|
||||||
buffer_usage: BufferUsage::INDEX,
|
buffer_usage: BufferUsage::INDEX,
|
||||||
..Default::default()
|
..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(
|
render_resource_context.set_asset_resource(
|
||||||
changed_mesh_handle,
|
changed_mesh_handle,
|
||||||
RenderResourceId::Buffer(index_buffer),
|
RenderResourceId::Buffer(index_buffer),
|
||||||
INDEX_BUFFER_ASSET_INDEX,
|
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
|
// TODO: remove this once batches are pipeline specific and deprecate assigned_meshes draw target
|
||||||
for (handle, mut render_pipelines) in query.iter_mut() {
|
for (handle, mut render_pipelines) in query.iter_mut() {
|
||||||
if let Some(mesh) = meshes.get(handle) {
|
if let Some(mesh) = meshes.get(handle) {
|
||||||
for render_pipeline in render_pipelines.pipelines.iter_mut() {
|
for render_pipeline in render_pipelines.pipelines.iter_mut() {
|
||||||
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
|
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(RenderResourceId::Buffer(vertex_buffer)) =
|
if let Some(RenderResourceId::Buffer(index_buffer_resource)) =
|
||||||
render_resource_context.get_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX)
|
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
|
||||||
{
|
{
|
||||||
render_pipelines.bindings.set_vertex_buffer(
|
// set index buffer into binding
|
||||||
"Vertex",
|
render_pipelines
|
||||||
vertex_buffer,
|
.bindings
|
||||||
render_resource_context
|
.set_index_buffer(index_buffer_resource);
|
||||||
.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
|
}
|
||||||
.and_then(|r| {
|
|
||||||
if let RenderResourceId::Buffer(buffer) = r {
|
if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
|
||||||
Some(buffer)
|
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
|
||||||
} else {
|
{
|
||||||
None
|
// 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)]
|
pub fn attributes_count_vertices(attributes: &VertexAttributesHashMap) -> Option<u32> {
|
||||||
mod tests {
|
let mut vertex_count: Option<u32> = None;
|
||||||
use super::{AsVertexBufferDescriptor, Mesh, VertexAttribute};
|
for (attribute_name, attribute_data) in attributes {
|
||||||
use crate::{mesh::Vertex, pipeline::PrimitiveTopology};
|
let attribute_len = attribute_data.len();
|
||||||
use bevy_core::AsBytes;
|
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]
|
// bundle into interleaved buffers
|
||||||
fn test_get_vertex_bytes() {
|
for vertex_index in 0..vertex_count {
|
||||||
let vertices = &[
|
let vertex_index = vertex_index as usize;
|
||||||
([0., 0., 0.], [1., 1., 1.], [2., 2.]),
|
for (attribute_descriptor, attributes_bytes) in &attributes_gpu_ready {
|
||||||
([3., 3., 3.], [4., 4., 4.], [5., 5.]),
|
let stride = attribute_descriptor.format.get_size() as usize;
|
||||||
([6., 6., 6.], [7., 7., 7.], [8., 8.]),
|
// insert one element
|
||||||
];
|
attributes_interleaved_buffer
|
||||||
|
.extend(&attributes_bytes[vertex_index * stride..vertex_index * stride + stride]);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mesh = Mesh {
|
let vertex_buffer_descriptor_reference = VertexBufferDescriptor {
|
||||||
primitive_topology: PrimitiveTopology::TriangleStrip,
|
name: Default::default(),
|
||||||
attributes: vec![
|
stride: accumulated_offset,
|
||||||
VertexAttribute::position(positions),
|
step_mode: InputStepMode::Vertex,
|
||||||
VertexAttribute::normal(normals),
|
attributes: attributes_gpu_ready.iter().map(|x| x.0.clone()).collect(),
|
||||||
VertexAttribute::uv(uvs),
|
|
||||||
],
|
|
||||||
indices: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected_vertices = &[
|
(
|
||||||
Vertex {
|
attributes_interleaved_buffer,
|
||||||
position: [0., 0., 0.],
|
vertex_buffer_descriptor_reference,
|
||||||
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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
mod mesh;
|
mod mesh;
|
||||||
mod vertex;
|
|
||||||
|
|
||||||
pub use mesh::*;
|
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,
|
CompareFunction, CullMode, DepthStencilStateDescriptor, FrontFace, IndexFormat,
|
||||||
PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor,
|
PrimitiveTopology, RasterizationStateDescriptor, StencilStateFaceDescriptor,
|
||||||
},
|
},
|
||||||
BindType, DynamicBinding, PipelineLayout, StencilStateDescriptor, VertexBufferDescriptors,
|
BindType, DynamicBinding, PipelineLayout, StencilStateDescriptor,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
shader::{Shader, ShaderStages},
|
shader::{Shader, ShaderStages},
|
||||||
@ -131,7 +131,6 @@ impl PipelineDescriptor {
|
|||||||
&mut self,
|
&mut self,
|
||||||
shaders: &Assets<Shader>,
|
shaders: &Assets<Shader>,
|
||||||
bevy_conventions: bool,
|
bevy_conventions: bool,
|
||||||
vertex_buffer_descriptors: Option<&VertexBufferDescriptors>,
|
|
||||||
dynamic_bindings: &[DynamicBinding],
|
dynamic_bindings: &[DynamicBinding],
|
||||||
) {
|
) {
|
||||||
let vertex_spirv = shaders.get(&self.shader_stages.vertex).unwrap();
|
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);
|
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() {
|
if !dynamic_bindings.is_empty() {
|
||||||
// set binding uniforms to dynamic if render resource bindings use dynamic
|
// set binding uniforms to dynamic if render resource bindings use dynamic
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
use super::{
|
use super::{state_descriptors::PrimitiveTopology, IndexFormat, PipelineDescriptor};
|
||||||
state_descriptors::PrimitiveTopology, IndexFormat, PipelineDescriptor, VertexBufferDescriptors,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
pipeline::{
|
||||||
|
InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat,
|
||||||
|
VERTEX_FALLBACK_LAYOUT_NAME,
|
||||||
|
},
|
||||||
renderer::RenderResourceContext,
|
renderer::RenderResourceContext,
|
||||||
shader::{Shader, ShaderSource},
|
shader::{Shader, ShaderSource},
|
||||||
};
|
};
|
||||||
@ -10,6 +12,7 @@ use bevy_property::{Properties, Property};
|
|||||||
use bevy_utils::{HashMap, HashSet};
|
use bevy_utils::{HashMap, HashSet};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug, Properties)]
|
#[derive(Clone, Eq, PartialEq, Debug, Properties)]
|
||||||
pub struct PipelineSpecialization {
|
pub struct PipelineSpecialization {
|
||||||
@ -17,6 +20,7 @@ pub struct PipelineSpecialization {
|
|||||||
pub primitive_topology: PrimitiveTopology,
|
pub primitive_topology: PrimitiveTopology,
|
||||||
pub dynamic_bindings: Vec<DynamicBinding>,
|
pub dynamic_bindings: Vec<DynamicBinding>,
|
||||||
pub index_format: IndexFormat,
|
pub index_format: IndexFormat,
|
||||||
|
pub mesh_attribute_layout: VertexBufferDescriptor,
|
||||||
pub sample_count: u32,
|
pub sample_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ impl Default for PipelineSpecialization {
|
|||||||
primitive_topology: Default::default(),
|
primitive_topology: Default::default(),
|
||||||
dynamic_bindings: Default::default(),
|
dynamic_bindings: Default::default(),
|
||||||
index_format: IndexFormat::Uint32,
|
index_format: IndexFormat::Uint32,
|
||||||
|
mesh_attribute_layout: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +142,6 @@ impl PipelineCompiler {
|
|||||||
pipelines: &mut Assets<PipelineDescriptor>,
|
pipelines: &mut Assets<PipelineDescriptor>,
|
||||||
shaders: &mut Assets<Shader>,
|
shaders: &mut Assets<Shader>,
|
||||||
source_pipeline: &Handle<PipelineDescriptor>,
|
source_pipeline: &Handle<PipelineDescriptor>,
|
||||||
vertex_buffer_descriptors: &VertexBufferDescriptors,
|
|
||||||
pipeline_specialization: &PipelineSpecialization,
|
pipeline_specialization: &PipelineSpecialization,
|
||||||
) -> Handle<PipelineDescriptor> {
|
) -> Handle<PipelineDescriptor> {
|
||||||
let source_descriptor = pipelines.get(source_pipeline).unwrap();
|
let source_descriptor = pipelines.get(source_pipeline).unwrap();
|
||||||
@ -162,10 +166,62 @@ impl PipelineCompiler {
|
|||||||
specialized_descriptor.reflect_layout(
|
specialized_descriptor.reflect_layout(
|
||||||
shaders,
|
shaders,
|
||||||
true,
|
true,
|
||||||
Some(vertex_buffer_descriptors),
|
|
||||||
&pipeline_specialization.dynamic_bindings,
|
&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.sample_count = pipeline_specialization.sample_count;
|
||||||
specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology;
|
specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology;
|
||||||
specialized_descriptor.index_format = pipeline_specialization.index_format;
|
specialized_descriptor.index_format = pipeline_specialization.index_format;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
use super::{BindGroupDescriptor, VertexBufferDescriptor, VertexBufferDescriptors};
|
use super::{BindGroupDescriptor, VertexBufferDescriptor};
|
||||||
use crate::shader::{ShaderLayout, GL_VERTEX_INDEX};
|
use crate::shader::ShaderLayout;
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
@ -67,27 +67,6 @@ impl PipelineLayout {
|
|||||||
vertex_buffer_descriptors,
|
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)]
|
#[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() {
|
for pipeline in render_pipelines.pipelines.iter_mut() {
|
||||||
pipeline.specialization.sample_count = msaa.samples;
|
pipeline.specialization.sample_count = msaa.samples;
|
||||||
pipeline.specialization.index_format = index_format;
|
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() {
|
for render_pipeline in render_pipelines.pipelines.iter() {
|
||||||
@ -123,6 +128,7 @@ pub fn draw_render_pipelines_system(
|
|||||||
draw_context
|
draw_context
|
||||||
.set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings])
|
.set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if let Some(indices) = index_range.clone() {
|
if let Some(indices) = index_range.clone() {
|
||||||
draw.draw_indexed(indices, 0, 0..1);
|
draw.draw_indexed(indices, 0, 0..1);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
use super::VertexFormat;
|
use super::VertexFormat;
|
||||||
use bevy_utils::HashMap;
|
use bevy_property::Property;
|
||||||
use std::borrow::Cow;
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
pub use bevy_derive::AsVertexBufferDescriptor;
|
#[derive(Clone, Debug, Eq, PartialEq, Default, Property, Serialize, Deserialize)]
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
||||||
pub struct VertexBufferDescriptor {
|
pub struct VertexBufferDescriptor {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
pub stride: u64,
|
pub stride: u64,
|
||||||
@ -12,33 +14,33 @@ pub struct VertexBufferDescriptor {
|
|||||||
pub attributes: Vec<VertexAttributeDescriptor>,
|
pub attributes: Vec<VertexAttributeDescriptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const VERTEX_FALLBACK_LAYOUT_NAME: &str = "Fallback";
|
||||||
impl VertexBufferDescriptor {
|
impl VertexBufferDescriptor {
|
||||||
pub fn sync_with_descriptor(&mut self, descriptor: &VertexBufferDescriptor) {
|
pub fn new_from_attribute(
|
||||||
for attribute in self.attributes.iter_mut() {
|
attribute: VertexAttributeDescriptor,
|
||||||
let descriptor_attribute = descriptor
|
step_mode: InputStepMode,
|
||||||
.attributes
|
) -> VertexBufferDescriptor {
|
||||||
.iter()
|
VertexBufferDescriptor {
|
||||||
.find(|a| a.name == attribute.name)
|
name: attribute.name.clone(),
|
||||||
.unwrap_or_else(|| {
|
stride: attribute.format.get_size(),
|
||||||
panic!(
|
step_mode,
|
||||||
"Encountered unsupported Vertex Buffer Attribute: {}",
|
attributes: vec![attribute.clone()],
|
||||||
attribute.name
|
|
||||||
);
|
|
||||||
});
|
|
||||||
attribute.offset = descriptor_attribute.offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.stride = descriptor.stride;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum InputStepMode {
|
pub enum InputStepMode {
|
||||||
Vertex = 0,
|
Vertex = 0,
|
||||||
Instance = 1,
|
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 struct VertexAttributeDescriptor {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
pub offset: u64,
|
pub offset: u64,
|
||||||
@ -46,24 +48,9 @@ pub struct VertexAttributeDescriptor {
|
|||||||
pub shader_location: u32,
|
pub shader_location: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
/// Internally, `bevy_render` uses hashes to identify vertex attribute names.
|
||||||
pub struct VertexBufferDescriptors {
|
pub fn get_vertex_attribute_name_id(name: &str) -> u64 {
|
||||||
pub descriptors: HashMap<String, VertexBufferDescriptor>,
|
let mut hasher = bevy_utils::AHasher::default();
|
||||||
}
|
hasher.write(&name.as_bytes());
|
||||||
|
hasher.finish()
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use crate::Color;
|
use crate::Color;
|
||||||
use bevy_math::{Mat4, Vec2, Vec3, Vec4};
|
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 {
|
pub enum VertexFormat {
|
||||||
Uchar2 = 1,
|
Uchar2 = 1,
|
||||||
Uchar4 = 3,
|
Uchar4 = 3,
|
||||||
|
|||||||
@ -15,7 +15,7 @@ use std::{ops::Range, sync::Arc};
|
|||||||
pub struct HeadlessRenderResourceContext {
|
pub struct HeadlessRenderResourceContext {
|
||||||
buffer_info: Arc<RwLock<HashMap<BufferId, BufferInfo>>>,
|
buffer_info: Arc<RwLock<HashMap<BufferId, BufferInfo>>>,
|
||||||
texture_descriptors: Arc<RwLock<HashMap<TextureId, TextureDescriptor>>>,
|
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 {
|
impl HeadlessRenderResourceContext {
|
||||||
@ -92,7 +92,7 @@ impl RenderResourceContext for HeadlessRenderResourceContext {
|
|||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
render_resource: RenderResourceId,
|
render_resource: RenderResourceId,
|
||||||
index: usize,
|
index: u64,
|
||||||
) {
|
) {
|
||||||
self.asset_resources
|
self.asset_resources
|
||||||
.write()
|
.write()
|
||||||
@ -102,7 +102,7 @@ impl RenderResourceContext for HeadlessRenderResourceContext {
|
|||||||
fn get_asset_resource_untyped(
|
fn get_asset_resource_untyped(
|
||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
index: usize,
|
index: u64,
|
||||||
) -> Option<RenderResourceId> {
|
) -> Option<RenderResourceId> {
|
||||||
self.asset_resources.write().get(&(handle, index)).cloned()
|
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 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));
|
self.asset_resources.write().remove(&(handle, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@ use crate::{
|
|||||||
use bevy_asset::{Asset, Handle, HandleUntyped};
|
use bevy_asset::{Asset, Handle, HandleUntyped};
|
||||||
use bevy_utils::{HashMap, HashSet};
|
use bevy_utils::{HashMap, HashSet};
|
||||||
use std::{hash::Hash, ops::Range};
|
use std::{hash::Hash, ops::Range};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, Debug)]
|
#[derive(Clone, Eq, Debug)]
|
||||||
pub enum RenderResourceBinding {
|
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
|
// 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)]
|
#[derive(Eq, PartialEq, Debug, Default, Clone)]
|
||||||
pub struct RenderResourceBindings {
|
pub struct RenderResourceBindings {
|
||||||
// TODO: remove this. it shouldn't be needed anymore
|
|
||||||
pub id: RenderResourceBindingsId,
|
|
||||||
bindings: HashMap<String, RenderResourceBinding>,
|
bindings: HashMap<String, RenderResourceBinding>,
|
||||||
// TODO: remove this
|
/// A Buffer that contains all attributes a mesh has defined
|
||||||
vertex_buffers: HashMap<String, (BufferId, Option<BufferId>)>,
|
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_groups: HashMap<BindGroupId, BindGroup>,
|
||||||
bind_group_descriptors: HashMap<BindGroupDescriptorId, Option<BindGroupId>>,
|
bind_group_descriptors: HashMap<BindGroupDescriptorId, Option<BindGroupId>>,
|
||||||
dirty_bind_groups: HashSet<BindGroupId>,
|
dirty_bind_groups: HashSet<BindGroupId>,
|
||||||
@ -139,25 +139,10 @@ impl RenderResourceBindings {
|
|||||||
for (name, binding) in render_resource_bindings.bindings.iter() {
|
for (name, binding) in render_resource_bindings.bindings.iter() {
|
||||||
self.set(name, binding.clone());
|
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>)> {
|
pub fn set_index_buffer(&mut self, index_buffer: BufferId) {
|
||||||
self.vertex_buffers.get(name).cloned()
|
self.index_buffer = Some(index_buffer);
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bind_group(&mut self, descriptor: &BindGroupDescriptor) -> BindGroupStatus {
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@ -38,14 +38,14 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
resource: RenderResourceId,
|
resource: RenderResourceId,
|
||||||
index: usize,
|
index: u64,
|
||||||
);
|
);
|
||||||
fn get_asset_resource_untyped(
|
fn get_asset_resource_untyped(
|
||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
index: usize,
|
index: u64,
|
||||||
) -> Option<RenderResourceId>;
|
) -> 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(
|
fn create_render_pipeline(
|
||||||
&self,
|
&self,
|
||||||
pipeline_handle: Handle<PipelineDescriptor>,
|
pipeline_handle: Handle<PipelineDescriptor>,
|
||||||
@ -63,29 +63,21 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl dyn RenderResourceContext {
|
impl dyn RenderResourceContext {
|
||||||
pub fn set_asset_resource<T>(
|
pub fn set_asset_resource<T>(&self, handle: &Handle<T>, resource: RenderResourceId, index: u64)
|
||||||
&self,
|
where
|
||||||
handle: &Handle<T>,
|
|
||||||
resource: RenderResourceId,
|
|
||||||
index: usize,
|
|
||||||
) where
|
|
||||||
T: Asset,
|
T: Asset,
|
||||||
{
|
{
|
||||||
self.set_asset_resource_untyped(handle.clone_weak_untyped(), resource, index);
|
self.set_asset_resource_untyped(handle.clone_weak_untyped(), resource, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_asset_resource<T>(
|
pub fn get_asset_resource<T>(&self, handle: &Handle<T>, index: u64) -> Option<RenderResourceId>
|
||||||
&self,
|
|
||||||
handle: &Handle<T>,
|
|
||||||
index: usize,
|
|
||||||
) -> Option<RenderResourceId>
|
|
||||||
where
|
where
|
||||||
T: Asset,
|
T: Asset,
|
||||||
{
|
{
|
||||||
self.get_asset_resource_untyped(handle.clone_weak_untyped(), index)
|
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
|
where
|
||||||
T: Asset,
|
T: Asset,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,12 +7,10 @@ use crate::{
|
|||||||
texture::{TextureComponentType, TextureViewDimension},
|
texture::{TextureComponentType, TextureViewDimension},
|
||||||
};
|
};
|
||||||
use bevy_core::AsBytes;
|
use bevy_core::AsBytes;
|
||||||
use bevy_utils::HashSet;
|
|
||||||
use spirv_reflect::{
|
use spirv_reflect::{
|
||||||
types::{
|
types::{
|
||||||
ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType, ReflectDimension,
|
ReflectDescriptorBinding, ReflectDescriptorSet, ReflectDescriptorType, ReflectDimension,
|
||||||
ReflectInterfaceVariable, ReflectShaderStageFlags, ReflectTypeDescription,
|
ReflectShaderStageFlags, ReflectTypeDescription, ReflectTypeFlags,
|
||||||
ReflectTypeFlags,
|
|
||||||
},
|
},
|
||||||
ShaderModule,
|
ShaderModule,
|
||||||
};
|
};
|
||||||
@ -21,6 +19,7 @@ impl ShaderLayout {
|
|||||||
pub fn from_spirv(spirv_data: &[u32], bevy_conventions: bool) -> ShaderLayout {
|
pub fn from_spirv(spirv_data: &[u32], bevy_conventions: bool) -> ShaderLayout {
|
||||||
match ShaderModule::load_u8_data(spirv_data.as_bytes()) {
|
match ShaderModule::load_u8_data(spirv_data.as_bytes()) {
|
||||||
Ok(ref mut module) => {
|
Ok(ref mut module) => {
|
||||||
|
// init
|
||||||
let entry_point_name = module.get_entry_point_name();
|
let entry_point_name = module.get_entry_point_name();
|
||||||
let shader_stage = module.get_shader_stage();
|
let shader_stage = module.get_shader_stage();
|
||||||
let mut bind_groups = Vec::new();
|
let mut bind_groups = Vec::new();
|
||||||
@ -29,66 +28,45 @@ impl ShaderLayout {
|
|||||||
bind_groups.push(bind_group);
|
bind_groups.push(bind_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// obtain attribute descriptors from reflection
|
||||||
let mut vertex_attribute_descriptors = Vec::new();
|
let mut vertex_attribute_descriptors = Vec::new();
|
||||||
for input_variable in module.enumerate_input_variables(None).unwrap() {
|
for input_variable in module.enumerate_input_variables(None).unwrap() {
|
||||||
let vertex_attribute_descriptor =
|
if input_variable.name == GL_VERTEX_INDEX {
|
||||||
reflect_vertex_attribute_descriptor(&input_variable);
|
|
||||||
if vertex_attribute_descriptor.name == GL_VERTEX_INDEX {
|
|
||||||
continue;
|
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
|
vertex_attribute_descriptors
|
||||||
.sort_by(|a, b| a.shader_location.cmp(&b.shader_location));
|
.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 vertex_buffer_descriptors = Vec::new();
|
||||||
let mut current_descriptor: Option<VertexBufferDescriptor> = None;
|
|
||||||
for vertex_attribute_descriptor in vertex_attribute_descriptors.drain(..) {
|
for vertex_attribute_descriptor in vertex_attribute_descriptors.drain(..) {
|
||||||
let mut instance = false;
|
let mut instance = false;
|
||||||
|
// obtain buffer name and instancing flag
|
||||||
let current_buffer_name = {
|
let current_buffer_name = {
|
||||||
if bevy_conventions {
|
if bevy_conventions {
|
||||||
if vertex_attribute_descriptor.name == GL_VERTEX_INDEX {
|
if vertex_attribute_descriptor.name == GL_VERTEX_INDEX {
|
||||||
GL_VERTEX_INDEX.to_string()
|
GL_VERTEX_INDEX.to_string()
|
||||||
} else {
|
} else {
|
||||||
let parts = vertex_attribute_descriptor
|
instance = vertex_attribute_descriptor.name.starts_with("I_");
|
||||||
.name
|
vertex_attribute_descriptor.name.to_string()
|
||||||
.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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
"DefaultVertex".to_string()
|
"DefaultVertex".to_string()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(current) = current_descriptor.as_mut() {
|
// create a new buffer descriptor, per attribute!
|
||||||
if current.name == current_buffer_name {
|
vertex_buffer_descriptors.push(VertexBufferDescriptor {
|
||||||
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 {
|
|
||||||
attributes: vec![vertex_attribute_descriptor],
|
attributes: vec![vertex_attribute_descriptor],
|
||||||
name: current_buffer_name.into(),
|
name: current_buffer_name.into(),
|
||||||
step_mode: if instance {
|
step_mode: if instance {
|
||||||
@ -97,16 +75,7 @@ impl ShaderLayout {
|
|||||||
InputStepMode::Vertex
|
InputStepMode::Vertex
|
||||||
},
|
},
|
||||||
stride: 0,
|
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 {
|
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(
|
fn reflect_bind_group(
|
||||||
descriptor_set: &ReflectDescriptorSet,
|
descriptor_set: &ReflectDescriptorSet,
|
||||||
shader_stage: ReflectShaderStageFlags,
|
shader_stage: ReflectShaderStageFlags,
|
||||||
@ -352,6 +300,12 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::shader::{Shader, ShaderStage};
|
use crate::shader::{Shader, ShaderStage};
|
||||||
|
|
||||||
|
impl VertexBufferDescriptor {
|
||||||
|
pub fn test_zero_stride(mut self) -> VertexBufferDescriptor {
|
||||||
|
self.stride = 0;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_reflection() {
|
fn test_reflection() {
|
||||||
let vertex_shader = Shader::from_glsl(
|
let vertex_shader = Shader::from_glsl(
|
||||||
@ -382,36 +336,36 @@ mod tests {
|
|||||||
ShaderLayout {
|
ShaderLayout {
|
||||||
entry_point: "main".into(),
|
entry_point: "main".into(),
|
||||||
vertex_buffer_descriptors: vec![
|
vertex_buffer_descriptors: vec![
|
||||||
VertexBufferDescriptor {
|
VertexBufferDescriptor::new_from_attribute(
|
||||||
name: "Vertex".into(),
|
|
||||||
attributes: vec![
|
|
||||||
VertexAttributeDescriptor {
|
VertexAttributeDescriptor {
|
||||||
name: "Vertex_Position".into(),
|
name: "Vertex_Position".into(),
|
||||||
format: VertexFormat::Float4,
|
format: VertexFormat::Float4,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
shader_location: 0,
|
shader_location: 0,
|
||||||
},
|
},
|
||||||
|
InputStepMode::Vertex
|
||||||
|
)
|
||||||
|
.test_zero_stride(),
|
||||||
|
VertexBufferDescriptor::new_from_attribute(
|
||||||
VertexAttributeDescriptor {
|
VertexAttributeDescriptor {
|
||||||
name: "Vertex_Normal".into(),
|
name: "Vertex_Normal".into(),
|
||||||
format: VertexFormat::Uint4,
|
format: VertexFormat::Uint4,
|
||||||
offset: 16,
|
offset: 0,
|
||||||
shader_location: 1,
|
shader_location: 1,
|
||||||
}
|
|
||||||
],
|
|
||||||
step_mode: InputStepMode::Vertex,
|
|
||||||
stride: 32,
|
|
||||||
},
|
},
|
||||||
VertexBufferDescriptor {
|
InputStepMode::Vertex
|
||||||
name: "TestInstancing".into(),
|
)
|
||||||
attributes: vec![VertexAttributeDescriptor {
|
.test_zero_stride(),
|
||||||
|
VertexBufferDescriptor::new_from_attribute(
|
||||||
|
VertexAttributeDescriptor {
|
||||||
name: "I_TestInstancing_Property".into(),
|
name: "I_TestInstancing_Property".into(),
|
||||||
format: VertexFormat::Uint4,
|
format: VertexFormat::Uint4,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
shader_location: 2,
|
shader_location: 2,
|
||||||
},],
|
},
|
||||||
step_mode: InputStepMode::Instance,
|
InputStepMode::Instance
|
||||||
stride: 16,
|
)
|
||||||
}
|
.test_zero_stride(),
|
||||||
],
|
],
|
||||||
bind_groups: vec![
|
bind_groups: vec![
|
||||||
BindGroupDescriptor::new(
|
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_type_registry::TypeUuid;
|
||||||
use bevy_utils::HashSet;
|
use bevy_utils::HashSet;
|
||||||
|
|
||||||
pub const TEXTURE_ASSET_INDEX: usize = 0;
|
pub const TEXTURE_ASSET_INDEX: u64 = 0;
|
||||||
pub const SAMPLER_ASSET_INDEX: usize = 1;
|
pub const SAMPLER_ASSET_INDEX: u64 = 1;
|
||||||
|
|
||||||
#[derive(Debug, Clone, TypeUuid)]
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
|
#[uuid = "6ea26da6-6cf8-4ea2-9986-1d7bf6c17d6f"]
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use bevy_render::{
|
|||||||
color::Color,
|
color::Color,
|
||||||
draw::{Draw, DrawContext, DrawError, Drawable},
|
draw::{Draw, DrawContext, DrawError, Drawable},
|
||||||
mesh,
|
mesh,
|
||||||
pipeline::PipelineSpecialization,
|
pipeline::{PipelineSpecialization, VertexBufferDescriptor},
|
||||||
prelude::Msaa,
|
prelude::Msaa,
|
||||||
renderer::{
|
renderer::{
|
||||||
AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings,
|
AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings,
|
||||||
@ -41,6 +41,7 @@ pub struct DrawableText<'a> {
|
|||||||
pub style: &'a TextStyle,
|
pub style: &'a TextStyle,
|
||||||
pub text: &'a str,
|
pub text: &'a str,
|
||||||
pub msaa: &'a Msaa,
|
pub msaa: &'a Msaa,
|
||||||
|
pub font_quad_vertex_descriptor: &'a VertexBufferDescriptor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Drawable for DrawableText<'a> {
|
impl<'a> Drawable for DrawableText<'a> {
|
||||||
@ -50,16 +51,21 @@ impl<'a> Drawable for DrawableText<'a> {
|
|||||||
&bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE,
|
&bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE,
|
||||||
&PipelineSpecialization {
|
&PipelineSpecialization {
|
||||||
sample_count: self.msaa.samples,
|
sample_count: self.msaa.samples,
|
||||||
|
mesh_attribute_layout: self.font_quad_vertex_descriptor.clone(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let render_resource_context = &**context.render_resource_context;
|
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;
|
let mut indices = 0..0;
|
||||||
if let Some(RenderResourceId::Buffer(quad_index_buffer)) = render_resource_context
|
if let Some(RenderResourceId::Buffer(quad_index_buffer)) = render_resource_context
|
||||||
.get_asset_resource(&bevy_sprite::QUAD_HANDLE, mesh::INDEX_BUFFER_ASSET_INDEX)
|
.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(0, transform_buffer)
|
||||||
.add_binding(1, sprite_buffer)
|
.add_binding(1, sprite_buffer)
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
context.create_bind_group_resource(2, &sprite_bind_group)?;
|
context.create_bind_group_resource(2, &sprite_bind_group)?;
|
||||||
draw.set_bind_group(2, &sprite_bind_group);
|
draw.set_bind_group(2, &sprite_bind_group);
|
||||||
draw.draw_indexed(indices.clone(), 0, 0..1);
|
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_math::Size;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
draw::{Draw, DrawContext, Drawable},
|
draw::{Draw, DrawContext, Drawable},
|
||||||
|
mesh::Mesh,
|
||||||
prelude::Msaa,
|
prelude::Msaa,
|
||||||
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
|
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
|
||||||
texture::Texture,
|
texture::Texture,
|
||||||
};
|
};
|
||||||
use bevy_sprite::TextureAtlas;
|
use bevy_sprite::{TextureAtlas, QUAD_HANDLE};
|
||||||
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
|
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
|
||||||
use bevy_transform::prelude::GlobalTransform;
|
use bevy_transform::prelude::GlobalTransform;
|
||||||
|
|
||||||
@ -94,10 +95,20 @@ pub fn draw_text_system(
|
|||||||
msaa: Res<Msaa>,
|
msaa: Res<Msaa>,
|
||||||
font_atlas_sets: Res<Assets<FontAtlasSet>>,
|
font_atlas_sets: Res<Assets<FontAtlasSet>>,
|
||||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||||
|
meshes: Res<Assets<Mesh>>,
|
||||||
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
||||||
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
|
mut asset_render_resource_bindings: ResMut<AssetRenderResourceBindings>,
|
||||||
mut query: Query<(&mut Draw, &Text, &Node, &GlobalTransform)>,
|
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() {
|
for (mut draw, text, node, global_transform) in query.iter_mut() {
|
||||||
if let Some(font) = fonts.get(&text.font) {
|
if let Some(font) = fonts.get(&text.font) {
|
||||||
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
|
let position = global_transform.translation - (node.size / 2.0).extend(0.0);
|
||||||
@ -112,6 +123,7 @@ pub fn draw_text_system(
|
|||||||
style: &text.style,
|
style: &text.style,
|
||||||
text: &text.value,
|
text: &text.value,
|
||||||
container_size: node.size,
|
container_size: node.size,
|
||||||
|
font_quad_vertex_descriptor: &font_quad_vertex_descriptor,
|
||||||
};
|
};
|
||||||
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
|
drawable_text.draw(&mut draw, &mut draw_context).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -311,7 +311,7 @@ impl RenderResourceContext for WgpuRenderResourceContext {
|
|||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
render_resource: RenderResourceId,
|
render_resource: RenderResourceId,
|
||||||
index: usize,
|
index: u64,
|
||||||
) {
|
) {
|
||||||
let mut asset_resources = self.resources.asset_resources.write();
|
let mut asset_resources = self.resources.asset_resources.write();
|
||||||
asset_resources.insert((handle, index), render_resource);
|
asset_resources.insert((handle, index), render_resource);
|
||||||
@ -320,13 +320,13 @@ impl RenderResourceContext for WgpuRenderResourceContext {
|
|||||||
fn get_asset_resource_untyped(
|
fn get_asset_resource_untyped(
|
||||||
&self,
|
&self,
|
||||||
handle: HandleUntyped,
|
handle: HandleUntyped,
|
||||||
index: usize,
|
index: u64,
|
||||||
) -> Option<RenderResourceId> {
|
) -> Option<RenderResourceId> {
|
||||||
let asset_resources = self.resources.asset_resources.read();
|
let asset_resources = self.resources.asset_resources.read();
|
||||||
asset_resources.get(&(handle, index)).cloned()
|
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();
|
let mut asset_resources = self.resources.asset_resources.write();
|
||||||
asset_resources.remove(&(handle, index));
|
asset_resources.remove(&(handle, index));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ pub struct WgpuResources {
|
|||||||
pub render_pipelines: Arc<RwLock<HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>>,
|
pub render_pipelines: Arc<RwLock<HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>>,
|
||||||
pub bind_groups: Arc<RwLock<HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>>,
|
pub bind_groups: Arc<RwLock<HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>>,
|
||||||
pub bind_group_layouts: Arc<RwLock<HashMap<BindGroupDescriptorId, wgpu::BindGroupLayout>>>,
|
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 {
|
impl WgpuResources {
|
||||||
|
|||||||
@ -103,6 +103,7 @@ impl WgpuFrom<&VertexBufferDescriptor> for OwnedWgpuVertexBufferDescriptor {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|a| a.wgpu_into())
|
.map(|a| a.wgpu_into())
|
||||||
.collect::<Vec<wgpu::VertexAttributeDescriptor>>();
|
.collect::<Vec<wgpu::VertexAttributeDescriptor>>();
|
||||||
|
|
||||||
OwnedWgpuVertexBufferDescriptor {
|
OwnedWgpuVertexBufferDescriptor {
|
||||||
step_mode: val.step_mode.wgpu_into(),
|
step_mode: val.step_mode.wgpu_into(),
|
||||||
stride: val.stride,
|
stride: val.stride,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user