Mesh overhaul with custom vertex attributes #592 (#599)

Mesh overhaul with custom vertex attributes
This commit is contained in:
Julian Heinken 2020-10-31 03:21:53 +01:00 committed by GitHub
parent ad940fbf6e
commit 4645da30c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 460 additions and 690 deletions

View File

@ -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]

View File

@ -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
}
}
})
}

View File

@ -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 {

View File

@ -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() {

View File

@ -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);
}
}
}

View File

@ -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>()

View File

@ -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),
}
}
pub fn normal(normals: Vec<[f32; 3]>) -> Self {
VertexAttribute {
name: Self::NORMAL.into(),
values: VertexAttributeValues::Float3(normals),
}
}
pub fn uv(uvs: Vec<[f32; 2]>) -> Self {
VertexAttribute {
name: Self::UV.into(),
values: VertexAttributeValues::Float2(uvs),
}
impl From<Vec<f32>> for VertexAttributeValues {
fn from(vec: Vec<f32>) -> Self {
VertexAttributeValues::Float(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; 2]>> for VertexAttributeValues {
fn from(vec: Vec<[f32; 2]>) -> Self {
VertexAttributeValues::Float2(vec)
}
}
impl From<Vec<[f32; 3]>> for VertexAttributeValues {
fn from(vec: Vec<[f32; 3]>) -> Self {
VertexAttributeValues::Float3(vec)
}
}
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(index_buffer_resource)) =
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
{
// set index buffer into binding
render_pipelines
.bindings
.set_index_buffer(index_buffer_resource);
}
if let Some(RenderResourceId::Buffer(vertex_buffer)) =
render_resource_context.get_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX)
if let Some(RenderResourceId::Buffer(vertex_attribute_buffer_resource)) =
render_resource_context.get_asset_resource(handle, VERTEX_ATTRIBUTE_BUFFER_ID)
{
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.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,
)
}

View File

@ -1,6 +1,4 @@
#[allow(clippy::module_inception)]
mod mesh;
mod vertex;
pub use mesh::*;
pub use vertex::*;

View File

@ -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 {}

View File

@ -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

View File

@ -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;

View File

@ -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)]

View File

@ -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);
}

View File

@ -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;
pub fn new_from_attribute(
attribute: VertexAttributeDescriptor,
step_mode: InputStepMode,
) -> VertexBufferDescriptor {
VertexBufferDescriptor {
name: attribute.name.clone(),
stride: attribute.format.get_size(),
step_mode,
attributes: vec![attribute.clone()],
}
self.stride = descriptor.stride;
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum InputStepMode {
Vertex = 0,
Instance = 1,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
impl Default for InputStepMode {
fn default() -> Self {
InputStepMode::Vertex
}
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct VertexAttributeDescriptor {
pub name: Cow<'static, str>,
pub offset: u64,
@ -46,24 +48,9 @@ pub struct VertexAttributeDescriptor {
pub shader_location: u32,
}
#[derive(Debug, Default)]
pub struct VertexBufferDescriptors {
pub descriptors: HashMap<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()
}

View File

@ -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,

View File

@ -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));
}

View File

@ -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::*;

View File

@ -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,
{

View File

@ -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(&current_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();
}
}

View File

@ -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"]

View File

@ -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);

View File

@ -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();
}

View File

@ -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));
}

View File

@ -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 {

View File

@ -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,