diff --git a/src/app/app_builder.rs b/src/app/app_builder.rs index 5b17de5f39..da2fb8d00b 100644 --- a/src/app/app_builder.rs +++ b/src/app/app_builder.rs @@ -199,8 +199,8 @@ impl AppBuilder { .add_resource_provider(LightResourceProvider::new(10)) .add_resource_provider(UiResourceProvider::new()) .add_resource_provider(MeshResourceProvider::new()) - .add_resource_provider(UniformResourceProvider::::new()) - .add_resource_provider(UniformResourceProvider::::new()) + .add_resource_provider(UniformResourceProviderNew::::new(false)) + .add_resource_provider(UniformResourceProviderNew::::new(false)) .add_forward_pass() .add_forward_pipeline() .add_ui_pipeline(); diff --git a/src/core/bytes.rs b/src/core/bytes.rs index df509d497c..227fe8fd9e 100644 --- a/src/core/bytes.rs +++ b/src/core/bytes.rs @@ -1,4 +1,8 @@ -use crate::{asset::Handle, math::Vec4, render::texture::Texture}; +use crate::{ + asset::Handle, + math::{Mat4, Vec4}, + render::texture::Texture, +}; use zerocopy::AsBytes; pub trait GetBytes { @@ -53,6 +57,20 @@ impl GetBytes for Vec4 { } } +impl GetBytes for Mat4 { + fn get_bytes(&self) -> Vec { + self.as_ref() + .as_bytes() + .iter() + .cloned() + .collect::>() + } + + fn get_bytes_ref(&self) -> Option<&[u8]> { + Some(self.as_ref().as_bytes()) + } +} + impl GetBytes for Handle { fn get_bytes(&self) -> Vec { Vec::new() diff --git a/src/render/pass/passes/forward.rs b/src/render/pass/passes/forward.rs index e11dc3f8ba..0b794253b2 100644 --- a/src/render/pass/passes/forward.rs +++ b/src/render/pass/passes/forward.rs @@ -27,7 +27,7 @@ impl<'a> ForwardPassBuilder for RenderGraphBuilder<'a> { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, - format: TextureFormat::Depth32Float, + format: TextureFormat::Depth32Float, // PERF: vulkan recommends using 24 bit depth for better performance usage: TextureUsage::OUTPUT_ATTACHMENT, }, )) diff --git a/src/render/render_resource/resource_providers/mod.rs b/src/render/render_resource/resource_providers/mod.rs index 855535e8da..bda54c6668 100644 --- a/src/render/render_resource/resource_providers/mod.rs +++ b/src/render/render_resource/resource_providers/mod.rs @@ -5,6 +5,7 @@ mod light_resource_provider; mod mesh_resource_provider; mod ui_resource_provider; mod uniform_resource_provider; +mod uniform_resource_provider_new; pub use camera2d_resource_provider::*; pub use camera_resource_provider::*; @@ -13,3 +14,4 @@ pub use light_resource_provider::*; pub use mesh_resource_provider::*; pub use ui_resource_provider::*; pub use uniform_resource_provider::*; +pub use uniform_resource_provider_new::*; diff --git a/src/render/render_resource/resource_providers/uniform_resource_provider_new.rs b/src/render/render_resource/resource_providers/uniform_resource_provider_new.rs new file mode 100644 index 0000000000..13afb33a2d --- /dev/null +++ b/src/render/render_resource/resource_providers/uniform_resource_provider_new.rs @@ -0,0 +1,589 @@ +use crate::{ + asset::{AssetStorage, Handle}, + render::{ + render_graph::RenderGraph, + render_resource::{ + AssetBatchers, BufferArrayInfo, BufferDynamicUniformInfo, BufferInfo, BufferUsage, + RenderResource, RenderResourceAssignments, RenderResourceAssignmentsProvider, + ResourceInfo, ResourceProvider, + }, + renderer::Renderer, + shader::{AsUniforms, FieldBindType}, + texture::{SamplerDescriptor, Texture, TextureDescriptor}, + Renderable, + }, +}; +use legion::{filter::*, prelude::*}; +use std::marker::PhantomData; +pub const BIND_BUFFER_ALIGNMENT: u64 = 256; + +#[derive(Debug)] +struct BufferArrayStatus { + new_item_count: usize, + item_size: usize, + staging_buffer_offset: usize, + buffer: Option, +} + +pub struct UniformResourceProviderNew +where + T: AsUniforms + Send + Sync + 'static, +{ + _marker: PhantomData, + use_dynamic_uniforms: bool, + is_instanceable: bool, + // PERF: somehow remove this HashSet + dynamic_uniform_buffer_status: Vec>, + instance_buffer_status: Option, + query: Option< + Query< + (Read, Write), + EntityFilterTuple< + And<(ComponentFilter, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, + >, + >, + query_finish: Option< + Query< + (Read, Write), + EntityFilterTuple< + And<(ComponentFilter, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, + >, + >, + handle_query: Option< + Query< + (Read>, Write), + EntityFilterTuple< + And<(ComponentFilter>, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, + >, + >, + handle_query_finish: Option< + Query< + (Read>, Write), + EntityFilterTuple< + And<(ComponentFilter>, ComponentFilter)>, + And<(Passthrough, Passthrough)>, + And<(Passthrough, Passthrough)>, + >, + >, + >, +} + +impl UniformResourceProviderNew +where + T: AsUniforms + Send + Sync + 'static, +{ + pub fn new(use_dynamic_uniforms: bool) -> Self { + let mut dynamic_uniform_buffer_status = Vec::new(); + let field_infos = T::get_field_infos(); + dynamic_uniform_buffer_status.resize_with(field_infos.len(), || None); + let is_instanceable = field_infos.iter().find(|f| f.is_instanceable).is_some(); + UniformResourceProviderNew { + dynamic_uniform_buffer_status, + use_dynamic_uniforms, + instance_buffer_status: None, + is_instanceable, + query: Some(<(Read, Write)>::query()), + query_finish: Some(<(Read, Write)>::query()), + handle_query: Some(<(Read>, Write)>::query()), + handle_query_finish: Some(<(Read>, Write)>::query()), + _marker: PhantomData, + } + } + + fn reset_buffer_array_status_counts(&mut self) { + for buffer_status in self.dynamic_uniform_buffer_status.iter_mut() { + if let Some((_name, buffer_status)) = buffer_status { + buffer_status.new_item_count = 0; + } + } + + if let Some(ref mut buffer_status) = self.instance_buffer_status { + buffer_status.new_item_count = 0; + } + } + + fn update_uniforms_info(&mut self, world: &mut World) { + if !self.use_dynamic_uniforms { + return; + } + + let query = self.query.take().unwrap(); + for (uniforms, mut renderable) in query.iter_mut(world) { + if !renderable.is_visible { + return; + } + + if renderable.is_instanced { + if self.is_instanceable { + self.increment_instance_count(|| Self::get_instance_size(&uniforms)); + } else { + panic!( + "Cannot instance uniforms of type {}", + std::any::type_name::() + ); + } + } else if self.use_dynamic_uniforms { + self.increment_dynamic_uniform_counts(&uniforms); + } + + Self::update_shader_defs( + &uniforms, + renderable.render_resource_assignments.as_mut().unwrap(), + ); + } + + self.query = Some(query); + } + + fn update_handles_info(&mut self, world: &mut World, resources: &Resources) { + let handle_query = self.handle_query.take().unwrap(); + let assets = resources.get::>(); + let mut asset_batchers = resources.get_mut::().unwrap(); + if let Some(assets) = assets { + for (entity, (handle, mut renderable)) in handle_query.iter_entities_mut(world) { + if !renderable.is_visible { + return; + } + + if renderable.is_instanced { + if self.is_instanceable { + asset_batchers.set_entity_handle(entity, *handle); + self.increment_instance_count(|| { + let uniforms = assets.get(&handle).unwrap(); + Self::get_instance_size(uniforms) + }); + } else { + panic!( + "Cannot instance uniforms of type Handle<{}>", + std::any::type_name::() + ); + } + } else { + let uniforms = assets + .get(&handle) + .expect("Handle points to a non-existent resource"); + Self::update_shader_defs( + uniforms, + renderable.render_resource_assignments.as_mut().unwrap(), + ); + if self.use_dynamic_uniforms { + self.increment_dynamic_uniform_counts(&uniforms); + } + } + } + } + + self.handle_query = Some(handle_query); + } + + fn increment_instance_count(&mut self, f: impl Fn() -> usize) { + if let Some(ref mut buffer_array_status) = self.instance_buffer_status { + buffer_array_status.new_item_count += 1; + } else { + self.instance_buffer_status = Some(BufferArrayStatus { + new_item_count: 1, + item_size: f(), + staging_buffer_offset: 0, + buffer: None, + }) + } + } + + fn get_instance_size(uniforms: &T) -> usize { + let mut instance_buffer_size = 0; + for field_info in T::get_field_infos().iter() { + if field_info.is_instanceable { + if let Some(FieldBindType::Uniform { size }) = + uniforms.get_field_bind_type(field_info.name) + { + instance_buffer_size += size; + } + } + } + + instance_buffer_size + } + + fn increment_dynamic_uniform_counts(&mut self, uniforms: &T) { + for (i, field_info) in T::get_field_infos().iter().enumerate() { + if let Some(FieldBindType::Uniform { size }) = + uniforms.get_field_bind_type(&field_info.name) + { + if let Some((ref _name, ref mut buffer_array_status)) = + self.dynamic_uniform_buffer_status[i] + { + buffer_array_status.new_item_count += 1; + } else { + self.dynamic_uniform_buffer_status[i] = Some(( + field_info.uniform_name.to_string(), + BufferArrayStatus { + new_item_count: 1, + item_size: size, + staging_buffer_offset: 0, + buffer: None, + }, + )) + } + } + } + } + + fn update_shader_defs( + uniforms: &T, + render_resource_assignments: &mut RenderResourceAssignments, + ) { + if let Some(shader_defs) = uniforms.get_shader_defs() { + for shader_def in shader_defs { + render_resource_assignments.shader_defs.insert(shader_def); + } + } + } + + fn setup_uniform_resources( + &mut self, + uniforms: &T, + renderer: &mut dyn Renderer, + resources: &Resources, + render_resource_assignments: &mut RenderResourceAssignments, + ) { + for field_info in T::get_field_infos() { + let bind_type = uniforms.get_field_bind_type(&field_info.name); + match bind_type { + Some(FieldBindType::Uniform { size }) => { + if self.use_dynamic_uniforms { + } else { + let buffer_resource = match render_resource_assignments + .get(field_info.uniform_name) + { + Some(render_resource) => render_resource, + None => { + let resource = renderer.create_buffer(BufferInfo { + size: size as u64, + buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM, + ..Default::default() + }); + render_resource_assignments.set(&field_info.uniform_name, resource); + resource + } + }; + + let (tmp_buffer, tmp_buffer_size) = if let Some(uniform_bytes) = + uniforms.get_uniform_bytes_ref(&field_info.uniform_name) + { + if size != uniform_bytes.len() { + panic!("The number of bytes produced for {} do not match the expected count. Actual: {}. Expected: {}.", field_info.uniform_name, uniform_bytes.len(), size); + } + + ( + renderer.create_buffer_mapped( + BufferInfo { + size: uniform_bytes.len() as u64, + buffer_usage: BufferUsage::COPY_SRC, + ..Default::default() + }, + &mut |mapped| { + mapped.copy_from_slice(uniform_bytes); + }, + ), + uniform_bytes.len(), + ) + } else if let Some(uniform_bytes) = + uniforms.get_uniform_bytes(field_info.uniform_name) + { + if size != uniform_bytes.len() { + panic!("The number of bytes produced for {} do not match the expected count. Actual: {}. Expected: {}.", field_info.uniform_name, uniform_bytes.len(), size); + } + + ( + renderer.create_buffer_mapped( + BufferInfo { + size: uniform_bytes.len() as u64, + buffer_usage: BufferUsage::COPY_SRC, + ..Default::default() + }, + &mut |mapped| { + mapped.copy_from_slice(&uniform_bytes); + }, + ), + uniform_bytes.len(), + ) + } else { + panic!( + "failed to get data from uniform: {}", + field_info.uniform_name + ); + }; + + renderer.copy_buffer_to_buffer( + tmp_buffer, + 0, + buffer_resource, + 0, + tmp_buffer_size as u64, + ); + + renderer.remove_buffer(tmp_buffer); + } + } + Some(FieldBindType::Texture) => { + let texture_handle = uniforms + .get_uniform_texture(&field_info.texture_name) + .unwrap(); + let (texture_resource, sampler_resource) = match renderer + .get_render_resources() + .get_texture_resource(texture_handle) + { + Some(texture_resource) => ( + texture_resource, + renderer + .get_render_resources() + .get_texture_sampler_resource(texture_handle) + .unwrap(), + ), + None => { + let storage = resources.get::>().unwrap(); + let texture = storage.get(&texture_handle).unwrap(); + + let texture_descriptor: TextureDescriptor = texture.into(); + let texture_resource = + renderer.create_texture(&texture_descriptor, Some(&texture.data)); + + let sampler_descriptor: SamplerDescriptor = texture.into(); + let sampler_resource = renderer.create_sampler(&sampler_descriptor); + + let render_resources = renderer.get_render_resources_mut(); + render_resources.set_texture_resource(texture_handle, texture_resource); + render_resources + .set_texture_sampler_resource(texture_handle, sampler_resource); + (texture_resource, sampler_resource) + } + }; + + render_resource_assignments.set(field_info.texture_name, texture_resource); + render_resource_assignments.set(field_info.sampler_name, sampler_resource); + } + None => {} + } + } + } + + fn setup_uniforms_resources( + &mut self, + world: &mut World, + resources: &Resources, + renderer: &mut dyn Renderer, + ) { + let query_finish = self.query_finish.take().unwrap(); + for (uniforms, mut renderable) in query_finish.iter_mut(world) { + if !renderable.is_visible { + return; + } + + if renderable.is_instanced { + panic!( + "Cannot instance uniforms of type {0}. Only Handle<{0}> can be instanced.", + std::any::type_name::() + ); + } else { + self.setup_uniform_resources( + &uniforms, + renderer, + resources, + renderable.render_resource_assignments.as_mut().unwrap(), + ) + } + } + + self.query_finish = Some(query_finish); + } + + fn setup_handles_resources( + &mut self, + world: &mut World, + resources: &Resources, + renderer: &mut dyn Renderer, + ) { + let assets = resources.get::>(); + if let Some(assets) = assets { + let handle_query_finish = self.handle_query_finish.take().unwrap(); + for (handle, mut renderable) in handle_query_finish.iter_mut(world) { + if !renderable.is_visible || renderable.is_instanced { + return; + } + + let uniforms = assets + .get(&handle) + .expect("Handle points to a non-existent resource"); + self.setup_uniform_resources( + &uniforms, + renderer, + resources, + renderable.render_resource_assignments.as_mut().unwrap(), + ) + } + + self.handle_query_finish = Some(handle_query_finish); + } + } + + fn setup_batched_resources( + &mut self, + world: &mut World, + resources: &Resources, + renderer: &mut dyn Renderer, + ) { + // update batch resources. this needs to run in "finish_update" because batches aren't finalized across + // all members of the batch until "UniformResourceProvider.update" has run for all members of the batch + if let Some(asset_storage) = resources.get::>() { + let mut asset_batchers = resources.get_mut::().unwrap(); + let mut render_resource_assignments_provider = resources + .get_mut::() + .unwrap(); + let handle_type = std::any::TypeId::of::(); + for batch in asset_batchers.get_handle_batches_mut::().unwrap() { + let handle: Handle = batch + .handles + .iter() + .find(|h| h.type_id == handle_type) + .map(|h| (*h).into()) + .unwrap(); + + let render_resource_assignments = batch + .render_resource_assignments + .get_or_insert_with(|| render_resource_assignments_provider.next()); + if let Some(uniforms) = asset_storage.get(&handle) { + self.setup_uniform_resources( + uniforms, + renderer, + resources, + render_resource_assignments, + ); + + Self::update_shader_defs(&uniforms, render_resource_assignments); + } + } + } + } + + fn setup_buffer_arrays(&mut self, renderer: &mut dyn Renderer) { + for buffer_array_status in self.dynamic_uniform_buffer_status.iter_mut() { + if let Some((name, buffer_array_status)) = buffer_array_status { + println!("dynamic {} {:?}", name, buffer_array_status); + Self::setup_buffer_array(buffer_array_status, renderer); + } + } + + if let Some(ref mut buffer_array_status) = self.instance_buffer_status { + println!("instance {}", std::any::type_name::()); + Self::setup_buffer_array(buffer_array_status, renderer); + } + } + + fn setup_buffer_array( + buffer_array_status: &mut BufferArrayStatus, + renderer: &mut dyn Renderer, + ) { + let new_capacity = if let Some(buffer) = buffer_array_status.buffer { + if let Some(ResourceInfo::Buffer(BufferInfo { + array_info: Some(array_info), + .. + })) = renderer.get_resource_info_mut(buffer) + { + if array_info.item_capacity < buffer_array_status.new_item_count as u64 { + Some( + buffer_array_status.new_item_count + buffer_array_status.new_item_count / 2, + ) + } else { + None + } + } else { + Some(buffer_array_status.new_item_count) + } + } else { + Some(buffer_array_status.new_item_count) + }; + + if let Some(new_capacity) = new_capacity { + println!("creating buffer {}", new_capacity); + let buffer = renderer.create_buffer(BufferInfo { + array_info: Some(BufferArrayInfo { + item_capacity: new_capacity as u64, + item_count: buffer_array_status.new_item_count as u64, + item_size: buffer_array_status.item_size as u64, + }), + size: (buffer_array_status.item_size * new_capacity) as u64, + buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM, + dynamic_uniform_info: Some(BufferDynamicUniformInfo::default()), + }); + + buffer_array_status.buffer = Some(buffer); + } + } + + fn initialize_vertex_buffer_descriptor(&self, render_graph: &mut RenderGraph) { + let vertex_buffer_descriptor = T::get_vertex_buffer_descriptor(); + if let Some(vertex_buffer_descriptor) = vertex_buffer_descriptor { + if let None = render_graph.get_vertex_buffer_descriptor(&vertex_buffer_descriptor.name) + { + render_graph.set_vertex_buffer_descriptor(vertex_buffer_descriptor.clone()); + } + } + } + + fn update_staging_buffer_offsets(&mut self) -> usize { + let mut size = 0; + for dynamic_buffer_array_status in self.dynamic_uniform_buffer_status.iter_mut() { + if let Some((_name, ref mut buffer_array_status)) = dynamic_buffer_array_status { + buffer_array_status.staging_buffer_offset = size; + size += buffer_array_status.item_size * buffer_array_status.new_item_count; + } + } + + size + } +} + +impl ResourceProvider for UniformResourceProviderNew +where + T: AsUniforms + Send + Sync + 'static, +{ + fn initialize( + &mut self, + renderer: &mut dyn Renderer, + world: &mut World, + resources: &Resources, + ) { + let mut render_graph = resources.get_mut::().unwrap(); + self.initialize_vertex_buffer_descriptor(&mut render_graph); + self.update(renderer, world, resources); + } + + fn update(&mut self, renderer: &mut dyn Renderer, world: &mut World, resources: &Resources) { + self.reset_buffer_array_status_counts(); + self.update_uniforms_info(world); + self.update_handles_info(world, resources); + } + + fn finish_update( + &mut self, + renderer: &mut dyn Renderer, + world: &mut World, + resources: &Resources, + ) { + let staging_buffer_size = self.update_staging_buffer_offsets(); + // TODO: when setting batch shader_defs, add INSTANCING + self.setup_buffer_arrays(renderer); + self.setup_uniforms_resources(world, resources, renderer); + self.setup_handles_resources(world, resources, renderer); + self.setup_batched_resources(world, resources, renderer); + } +} diff --git a/src/render/shader/uniform.rs b/src/render/shader/uniform.rs index e230fc3265..91988b8460 100644 --- a/src/render/shader/uniform.rs +++ b/src/render/shader/uniform.rs @@ -5,7 +5,7 @@ use crate::{ color::ColorSource, pipeline::{BindType, VertexBufferDescriptor}, texture::{Texture, TextureViewDimension}, - }, + }, prelude::Color, }; pub trait AsUniforms { @@ -32,7 +32,7 @@ impl ShaderDefSuffixProvider for bool { } pub enum FieldBindType { - Uniform, + Uniform { size: usize }, Texture, } @@ -79,7 +79,7 @@ where let bind_type = self.uniforms.get_field_bind_type(field_info.name); if let Some(bind_type) = bind_type { Some(match bind_type { - FieldBindType::Uniform => UniformInfo { + FieldBindType::Uniform { .. }=> UniformInfo { bind_type: BindType::Uniform { dynamic: false, properties: Vec::new(), @@ -119,10 +119,10 @@ pub trait AsFieldBindType { impl AsFieldBindType for ColorSource { fn get_field_bind_type(&self) -> Option { - Some(match *self { - ColorSource::Texture(_) => FieldBindType::Texture, - ColorSource::Color(_) => FieldBindType::Uniform, - }) + match *self { + ColorSource::Texture(_) => Some(FieldBindType::Texture), + ColorSource::Color(color) => color.get_field_bind_type(), + } } } @@ -145,8 +145,9 @@ impl AsFieldBindType for T where T: GetBytes, { + // TODO: this breaks if get_bytes_ref() isn't supported for a datatype default fn get_field_bind_type(&self) -> Option { - Some(FieldBindType::Uniform) + Some(FieldBindType::Uniform { size: self.get_bytes_ref().unwrap().len() }) } } diff --git a/src/render/shader/uniforms/local_to_world.rs b/src/render/shader/uniforms/local_to_world.rs index 3bf14229e4..c8c28d562c 100644 --- a/src/render/shader/uniforms/local_to_world.rs +++ b/src/render/shader/uniforms/local_to_world.rs @@ -1,15 +1,15 @@ use crate::{ asset::Handle, + core::GetBytes, render::{ pipeline::{ InputStepMode, VertexAttributeDescriptor, VertexBufferDescriptor, VertexFormat, }, - shader::{AsUniforms, FieldBindType, FieldInfo}, + shader::{AsUniforms, FieldBindType, FieldInfo, AsFieldBindType}, texture::Texture, }, }; use once_cell::sync::Lazy; -use zerocopy::AsBytes; static LOCAL_TO_WORLD_FIELD_INFOS: &[FieldInfo] = &[FieldInfo { name: "object", @@ -59,7 +59,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld { fn get_uniform_bytes(&self, name: &str) -> Option> { match name { - "Object" => Some(self.0.as_ref().as_bytes().into()), + "Object" => Some(self.0.get_bytes()), _ => None, } } @@ -69,7 +69,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld { } fn get_field_bind_type(&self, name: &str) -> Option { match name { - "object" => Some(FieldBindType::Uniform), + "object" => self.0.get_field_bind_type(), _ => None, } } @@ -79,7 +79,7 @@ impl AsUniforms for bevy_transform::prelude::LocalToWorld { fn get_uniform_bytes_ref(&self, name: &str) -> Option<&[u8]> { match name { - "Object" => Some(self.0.as_ref().as_bytes()), + "Object" => self.0.get_bytes_ref(), _ => None, } }