diff --git a/examples/simple_new_graph.rs b/examples/simple_new_graph.rs index f7848bdc10..3b7affc0a9 100644 --- a/examples/simple_new_graph.rs +++ b/examples/simple_new_graph.rs @@ -50,7 +50,6 @@ fn setup(world: &mut World) { uniform_selector::, uniform_selector::, ], - dynamic_uniform_indices: indices, }, local_to_world: LocalToWorld::identity(), translation: Translation::new(0.0, 0.0, 0.0), @@ -66,7 +65,6 @@ fn setup(world: &mut World) { uniform_selector::, uniform_selector::, ], - dynamic_uniform_indices: indices_2, }, local_to_world: LocalToWorld::identity(), translation: Translation::new(0.0, 0.0, 1.0), @@ -81,7 +79,6 @@ fn setup(world: &mut World) { uniform_selector::, uniform_selector::, ], - dynamic_uniform_indices: indices_3, }, local_to_world: LocalToWorld::identity(), translation: Translation::new(-2.0, 0.0, 1.0), diff --git a/src/render/render_graph_2/draw_target.rs b/src/render/render_graph_2/draw_target.rs index fbe11d8fd0..65cf1abade 100644 --- a/src/render/render_graph_2/draw_target.rs +++ b/src/render/render_graph_2/draw_target.rs @@ -22,7 +22,7 @@ pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) { let mut current_mesh_index_length = 0; let mesh_query = <(Read, Read>)>::query().filter(!component::()); - for (shader_uniforms, mesh) in mesh_query.iter(world) { + for (entity, (_shader_uniforms, mesh)) in mesh_query.iter_entities(world) { let mut should_load_mesh = current_mesh_id == None; if let Some(current) = current_mesh_id { should_load_mesh = current != mesh.id; @@ -51,7 +51,7 @@ pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) { } // TODO: validate bind group properties against shader uniform properties at least once - render_pass.setup_bind_groups(&&*shader_uniforms); + render_pass.setup_bind_groups(Some(&entity)); render_pass.draw_indexed(0..current_mesh_index_length, 0, 0..1); } diff --git a/src/render/render_graph_2/renderer.rs b/src/render/render_graph_2/renderer.rs index 4658758871..1cbcd7e7e7 100644 --- a/src/render/render_graph_2/renderer.rs +++ b/src/render/render_graph_2/renderer.rs @@ -1,4 +1,4 @@ -use crate::{legion::prelude::*, render::render_graph_2::{RenderGraph, ResourceInfo, PipelineDescriptor, ShaderUniforms}}; +use crate::{legion::prelude::*, render::render_graph_2::{RenderGraph, ResourceInfo, PipelineDescriptor}}; use std::ops::Range; pub trait Renderer { @@ -19,5 +19,5 @@ pub trait RenderPass { fn set_index_buffer(&mut self, name: &str, offset: u64); fn set_vertex_buffer(&mut self, start_slot: u32, name: &str, offset: u64); fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range); - fn setup_bind_groups(&mut self, shader_uniforms: &ShaderUniforms); + fn setup_bind_groups(&mut self, entity: Option<&Entity>); } \ No newline at end of file diff --git a/src/render/render_graph_2/shader.rs b/src/render/render_graph_2/shader.rs index e404fe4f2c..319ad1bf25 100644 --- a/src/render/render_graph_2/shader.rs +++ b/src/render/render_graph_2/shader.rs @@ -6,7 +6,6 @@ use crate::{ math::Vec4, render::render_graph_2::{BindType, UniformPropertyType}, }; -use std::collections::HashMap; use legion::storage::Component; use zerocopy::AsBytes; @@ -14,14 +13,12 @@ pub type ShaderUniformSelector = fn(Entity, &World) -> Option, - pub dynamic_uniform_indices: HashMap, } impl ShaderUniforms { pub fn new() -> Self { ShaderUniforms { uniform_selectors: Vec::new(), - dynamic_uniform_indices: HashMap::new(), } } diff --git a/src/render/render_graph_2/wgpu_renderer.rs b/src/render/render_graph_2/wgpu_renderer.rs index 31bac71a3e..a59a3a5752 100644 --- a/src/render/render_graph_2/wgpu_renderer.rs +++ b/src/render/render_graph_2/wgpu_renderer.rs @@ -13,6 +13,14 @@ use std::{ ops::Deref, }; +pub struct DynamicUniformBufferInfo { + pub indices: HashMap, + pub offsets: HashMap, + pub capacity: u64, + pub count: u64, + pub size: u64, +} + pub struct WgpuRenderer { pub device: wgpu::Device, pub queue: wgpu::Queue, @@ -24,6 +32,7 @@ pub struct WgpuRenderer { pub resource_info: HashMap, pub bind_groups: HashMap, pub bind_group_layouts: HashMap, + pub dynamic_uniform_buffer_info: HashMap, } impl WgpuRenderer { @@ -62,6 +71,7 @@ impl WgpuRenderer { resource_info: HashMap::new(), bind_groups: HashMap::new(), bind_group_layouts: HashMap::new(), + dynamic_uniform_buffer_info: HashMap::new(), } } @@ -268,7 +278,11 @@ impl WgpuRenderer { dynamic: _, properties: _, } => { - if let ResourceInfo::Buffer { size, buffer_usage: _ } = resource_info { + if let ResourceInfo::Buffer { + size, + buffer_usage: _, + } = resource_info + { let buffer = self.buffers.get(&b.name).unwrap(); wgpu::BindingResource::Buffer { buffer: buffer, @@ -303,9 +317,12 @@ impl WgpuRenderer { bind_group_id } - fn setup_dynamic_entity_shader_uniforms(&mut self, world: &World, render_graph: &RenderGraph, encoder: &mut wgpu::CommandEncoder) { - let mut dynamic_uniform_info = HashMap::new(); - + fn setup_dynamic_entity_shader_uniforms( + &mut self, + world: &World, + render_graph: &RenderGraph, + encoder: &mut wgpu::CommandEncoder, + ) { // retrieve all uniforms buffers that aren't aleady set. these are "dynamic" uniforms, which are set by the user in ShaderUniforms // TODO: this breaks down in multiple ways: // (1) resource_info will be set after the first run so this won't update. @@ -314,48 +331,86 @@ impl WgpuRenderer { for bind_group in pipeline.pipeline_layout.bind_groups.iter() { for binding in bind_group.bindings.iter() { // if let None = self.resource_info.get(&binding.name) { - if let BindType::Uniform { dynamic: true, .. } = &binding.bind_type { - if dynamic_uniform_info.contains_key(&binding.name) { - continue; - } - - dynamic_uniform_info.insert(binding.name.to_string(), UniformInfo { - size: binding.bind_type.get_uniform_size().unwrap(), - count: 0, - }); + if let BindType::Uniform { dynamic: true, .. } = &binding.bind_type { + if self.dynamic_uniform_buffer_info.contains_key(&binding.name) { + continue; } + + self.dynamic_uniform_buffer_info.insert( + binding.name.to_string(), + DynamicUniformBufferInfo { + capacity: 0, + count: 0, + size: binding.bind_type.get_uniform_size().unwrap(), + indices: HashMap::new(), + offsets: HashMap::new(), + }, + ); + } // } } } } - // count the number of entities providing each uniform - for (name, info) in dynamic_uniform_info.iter_mut() { + // count the number of entities providing each uniform + for (name, info) in self.dynamic_uniform_buffer_info.iter_mut() { + info.count = 0; for (entity, shader_uniforms) in >::query().iter_entities(world) { if let Some(_) = shader_uniforms.get_uniform_info(world, entity, name) { info.count += 1; - // TODO: assign indices to shader_uniforms here } } } - + // allocate uniform buffers - for (name, info) in dynamic_uniform_info.iter() { - let size = wgpu::BIND_BUFFER_ALIGNMENT * info.count; - if self.buffers.contains_key(name) { + for (name, info) in self.dynamic_uniform_buffer_info.iter_mut() { + if self.buffers.contains_key(name) && info.count < info.capacity { continue; } - self.create_buffer(name, size, wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM); + if info.count >= info.capacity && info.capacity != 0 { + panic!("resizing dynamic uniform buffers isn't supported yet. we still need to support updating bind groups"); + } + + // allocate enough space for twice as many entities as there are currently; + info.capacity = info.count * 2; + let size = wgpu::BIND_BUFFER_ALIGNMENT * info.capacity; + + // TODO: remove this code duplication in favor of self.create_buffer(). this will likely require a refactor + // the following is a flattening of the content in self.create_buffer(), which can't be called because + // of rust's ownership rules. sometimes rust makes me unhappy + + let buffer_usage = wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM; + let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { + size: size, + usage: buffer_usage, + }); + + self.resource_info.insert( + name.to_string(), + ResourceInfo::Buffer { + buffer_usage, + size: size, + }, + ); + + self.buffers.insert(name.to_string(), buffer); } // copy entity uniform data to buffers - for (name, info) in dynamic_uniform_info.iter_mut() { + for (name, info) in self.dynamic_uniform_buffer_info.iter_mut() { let size = wgpu::BIND_BUFFER_ALIGNMENT * info.count; - let mapped = self.device.create_buffer_mapped(size as usize, wgpu::BufferUsage::COPY_SRC); + let mapped = self + .device + .create_buffer_mapped(size as usize, wgpu::BufferUsage::COPY_SRC); let alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize; let mut offset = 0usize; - for (entity, shader_uniforms) in >::query().iter_entities(world) { + + for (i, (entity, shader_uniforms)) in >::query().iter_entities(world).enumerate() { + // TODO: check if index has changed. if it has, then entity should be updated + // TODO: only mem-map entities if their data has changed + info.offsets.insert(entity, offset as u64); + info.indices.insert(i, entity); if let Some(bytes) = shader_uniforms.get_uniform_bytes(world, entity, name) { mapped.data[offset..(offset + bytes.len())].copy_from_slice(bytes.as_slice()); offset += alignment; @@ -369,11 +424,6 @@ impl WgpuRenderer { } } -pub struct UniformInfo { - pub size: u64, - pub count: u64, -} - impl Renderer for WgpuRenderer { fn initialize(&mut self, world: &mut World, render_graph: &mut RenderGraph) { let (surface, window_size) = { @@ -423,7 +473,7 @@ impl Renderer for WgpuRenderer { let mut encoder = self .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); - + self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder); // setup, pipelines, bind groups, and resources @@ -555,7 +605,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> { } // TODO: maybe move setup to renderer.setup_bind_groups(&pipeline_desc); - fn setup_bind_groups(&mut self, shader_uniforms: &ShaderUniforms) { + fn setup_bind_groups(&mut self, entity: Option<&Entity>) { for (i, bind_group) in self .pipeline_descriptor .pipeline_layout @@ -571,19 +621,28 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> { let mut dynamic_uniform_indices = Vec::new(); for binding in bind_group.bindings.iter() { - if let BindType::Uniform { dynamic, ..} = binding.bind_type { + if let BindType::Uniform { dynamic, .. } = binding.bind_type { if !dynamic { continue; } - if let Some(index) = shader_uniforms.dynamic_uniform_indices.get(&binding.name) { + if let Some(dynamic_uniform_buffer_info) = + self.renderer.dynamic_uniform_buffer_info.get(&binding.name) + { + let index = dynamic_uniform_buffer_info + .offsets + .get(entity.unwrap()) + .unwrap(); dynamic_uniform_indices.push(*index); } } - } + } - self.render_pass - .set_bind_group(i as u32, &bind_group_info.bind_group, dynamic_uniform_indices.as_slice()); + self.render_pass.set_bind_group( + i as u32, + &bind_group_info.bind_group, + dynamic_uniform_indices.as_slice(), + ); } } }