From 7c2eb63a47df4c2d57cb0d0090a99fff2978d525 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 4 Feb 2020 00:06:17 -0800 Subject: [PATCH] improve performance dynamic uniforms --- src/render/render_graph_2/shader.rs | 89 +++++++++++++++------- src/render/render_graph_2/wgpu_renderer.rs | 28 +++++-- 2 files changed, 84 insertions(+), 33 deletions(-) diff --git a/src/render/render_graph_2/shader.rs b/src/render/render_graph_2/shader.rs index 663d88e010..b8ce621df5 100644 --- a/src/render/render_graph_2/shader.rs +++ b/src/render/render_graph_2/shader.rs @@ -261,8 +261,7 @@ impl ResourceProvider for UniformResourceProvider where T: AsUniforms + Send + Sync + 'static, { - fn initialize(&mut self, renderer: &mut dyn super::Renderer, world: &mut World) { - } + fn initialize(&mut self, renderer: &mut dyn super::Renderer, world: &mut World) {} fn update(&mut self, renderer: &mut dyn super::Renderer, world: &mut World) { let query = >::query(); @@ -270,62 +269,96 @@ where // TODO: this breaks down in multiple ways: // (1) resource_info will be set after the first run so this won't update. // (2) if we create new buffers, the old bind groups will be invalid + + // reset all uniform buffer info counts + for name in self.uniform_buffer_info_names.iter() { + renderer.get_dynamic_uniform_buffer_info_mut(name).unwrap().count = 0; + } + for uniforms in query.iter(world) { let uniform_layouts = uniforms.get_uniform_layouts(); for (i, uniform_info) in uniforms.get_uniform_infos().iter().enumerate() { if let None = renderer.get_dynamic_uniform_buffer_info(uniform_info.name) { let uniform_layout = uniform_layouts[i]; let mut info = DynamicUniformBufferInfo::new(); - info.size = uniform_layout.iter().map(|u| u.get_size()).fold(0, |total, current| total + current); - self.uniform_buffer_info_names.insert(uniform_info.name.to_string()); + info.size = uniform_layout + .iter() + .map(|u| u.get_size()) + .fold(0, |total, current| total + current); + self.uniform_buffer_info_names + .insert(uniform_info.name.to_string()); renderer.add_dynamic_uniform_buffer_info(uniform_info.name, info); } - let mut info = renderer.get_dynamic_uniform_buffer_info_mut(uniform_info.name) + let mut info = renderer + .get_dynamic_uniform_buffer_info_mut(uniform_info.name) .unwrap(); info.count += 1; } } // allocate uniform buffers - // for (name, info) in self.dynamic_uniform_buffer_infos.iter_mut() { - // if let Some(_) = renderer.get_resource_info(name) { - // continue; - // } + for name in self.uniform_buffer_info_names.iter() { + if let Some(_) = renderer.get_resource_info(name) { + continue; + } - // // 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; - // renderer.create_buffer(name, size, wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM) - // } + let info = renderer.get_dynamic_uniform_buffer_info_mut(name).unwrap(); + + // 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; + renderer.create_buffer( + name, + size, + wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::UNIFORM, + ); + } // copy entity uniform data to buffers for name in self.uniform_buffer_info_names.iter() { - let info = renderer.get_dynamic_uniform_buffer_info_mut(name).unwrap(); - info.capacity = info.count; - let size = wgpu::BIND_BUFFER_ALIGNMENT * info.count; - let mut data = vec![Default::default(); size as usize]; - // renderer - // .create_buffer_mapped("tmp_uniform_mapped", size as usize, wgpu::BufferUsage::COPY_SRC, &mut |mapped| { + let size = { + let info = renderer.get_dynamic_uniform_buffer_info(name).unwrap(); + wgpu::BIND_BUFFER_ALIGNMENT * info.count + }; let alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize; let mut offset = 0usize; - for (i, (entity, uniforms)) in query.iter_entities(world).enumerate() { + for (i, (entity, _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 + let info = renderer.get_dynamic_uniform_buffer_info_mut(name).unwrap(); info.offsets.insert(entity, offset as u64); info.indices.insert(i, entity); // TODO: try getting ref first - if let Some(uniform_bytes) = uniforms.get_uniform_bytes(name) { - data[offset..(offset + uniform_bytes.len())].copy_from_slice(uniform_bytes.as_slice()); - offset += alignment; - } + offset += alignment; } - // }); - + + // let mut data = vec![Default::default(); size as usize]; + renderer.create_buffer_mapped( + "tmp_uniform_mapped", + size as usize, + wgpu::BufferUsage::COPY_SRC, + &mut |mapped| { + let alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize; + let mut offset = 0usize; + for uniforms in query.iter(world) { + // TODO: check if index has changed. if it has, then entity should be updated + // TODO: only mem-map entities if their data has changed + // TODO: try getting ref first + if let Some(uniform_bytes) = uniforms.get_uniform_bytes(name) { + mapped[offset..(offset + uniform_bytes.len())] + .copy_from_slice(uniform_bytes.as_slice()); + offset += alignment; + } + } + }, + ); + // TODO: port me // let uniform_buffer = self.buffers.get(name); - renderer.create_buffer_with_data(name, &data, wgpu::BufferUsage::UNIFORM); + // renderer.create_buffer_with_data(name, &data, wgpu::BufferUsage::UNIFORM); + renderer.copy_buffer_to_buffer("tmp_uniform_mapped", 0, name, 0, size); } } diff --git a/src/render/render_graph_2/wgpu_renderer.rs b/src/render/render_graph_2/wgpu_renderer.rs index 5aa7ddd15f..9c57b982de 100644 --- a/src/render/render_graph_2/wgpu_renderer.rs +++ b/src/render/render_graph_2/wgpu_renderer.rs @@ -33,6 +33,7 @@ pub struct WgpuRenderer { pub device: wgpu::Device, pub queue: wgpu::Queue, pub surface: Option, + pub encoder: Option, pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub render_pipelines: HashMap, pub buffers: HashMap, @@ -72,6 +73,7 @@ impl WgpuRenderer { device, queue, surface: None, + encoder: None, swap_chain_descriptor, render_pipelines: HashMap::new(), buffers: HashMap::new(), @@ -465,18 +467,23 @@ impl Renderer for WgpuRenderer { } fn process_render_graph(&mut self, render_graph: &mut RenderGraph, world: &mut World) { + // TODO: this self.encoder handoff is a bit gross, but its here to give resource providers access to buffer copies without + // exposing the wgpu renderer internals to ResourceProvider traits. if this can be made cleaner that would be pretty cool. + self.encoder = Some(self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 })); + for resource_provider in render_graph.resource_providers.iter_mut() { resource_provider.update(self, world); } + let mut encoder = self.encoder.take().unwrap(); + let mut swap_chain = world.resources.get_mut::().unwrap(); let frame = swap_chain .get_next_texture() .expect("Timeout when acquiring next swap chain texture"); - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); // self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder); @@ -574,12 +581,24 @@ impl Renderer for WgpuRenderer { fn create_buffer_mapped(&mut self, name: &str, size: usize, buffer_usage: wgpu::BufferUsage, setup_data: &mut dyn FnMut(&mut [u8])) { let mut mapped = self.device.create_buffer_mapped(size, buffer_usage); setup_data(&mut mapped.data); - mapped.finish(); + let buffer = mapped.finish(); + + self.add_resource_info( + name, + ResourceInfo::Buffer { + buffer_usage, + size: size as u64, + }, + ); + + self.buffers.insert(name.to_string(), buffer); } fn copy_buffer_to_buffer(&mut self, source_buffer: &str, source_offset: u64, destination_buffer: &str, destination_offset: u64, size: u64) { let source = self.buffers.get(source_buffer).unwrap(); let destination = self.buffers.get(destination_buffer).unwrap(); + let encoder = self.encoder.as_mut().unwrap(); + encoder.copy_buffer_to_buffer(source, source_offset, destination, destination_offset, size); } fn get_dynamic_uniform_buffer_info(&self, name: &str) -> Option<&DynamicUniformBufferInfo> { self.dynamic_uniform_buffer_info.get(name) @@ -630,7 +649,6 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> { .draw_indexed(indices, base_vertex, instances); } - // TODO: maybe move setup to renderer.setup_bind_groups(&pipeline_desc); fn setup_bind_groups(&mut self, entity: Option<&Entity>) { for (i, bind_group) in self .pipeline_descriptor