dynamic uniform buffer info

This commit is contained in:
Carter Anderson 2020-01-28 00:36:51 -08:00
parent 6ba659049d
commit 0eb6c6fa74
5 changed files with 99 additions and 46 deletions

View File

@ -50,7 +50,6 @@ fn setup(world: &mut World) {
uniform_selector::<StandardMaterial>, uniform_selector::<StandardMaterial>,
uniform_selector::<LocalToWorld>, uniform_selector::<LocalToWorld>,
], ],
dynamic_uniform_indices: indices,
}, },
local_to_world: LocalToWorld::identity(), local_to_world: LocalToWorld::identity(),
translation: Translation::new(0.0, 0.0, 0.0), translation: Translation::new(0.0, 0.0, 0.0),
@ -66,7 +65,6 @@ fn setup(world: &mut World) {
uniform_selector::<StandardMaterial>, uniform_selector::<StandardMaterial>,
uniform_selector::<LocalToWorld>, uniform_selector::<LocalToWorld>,
], ],
dynamic_uniform_indices: indices_2,
}, },
local_to_world: LocalToWorld::identity(), local_to_world: LocalToWorld::identity(),
translation: Translation::new(0.0, 0.0, 1.0), translation: Translation::new(0.0, 0.0, 1.0),
@ -81,7 +79,6 @@ fn setup(world: &mut World) {
uniform_selector::<StandardMaterial>, uniform_selector::<StandardMaterial>,
uniform_selector::<LocalToWorld>, uniform_selector::<LocalToWorld>,
], ],
dynamic_uniform_indices: indices_3,
}, },
local_to_world: LocalToWorld::identity(), local_to_world: LocalToWorld::identity(),
translation: Translation::new(-2.0, 0.0, 1.0), translation: Translation::new(-2.0, 0.0, 1.0),

View File

@ -22,7 +22,7 @@ pub fn mesh_draw_target(world: &World, render_pass: &mut dyn RenderPass) {
let mut current_mesh_index_length = 0; let mut current_mesh_index_length = 0;
let mesh_query = let mesh_query =
<(Read<ShaderUniforms>, Read<Handle<Mesh>>)>::query().filter(!component::<Instanced>()); <(Read<ShaderUniforms>, Read<Handle<Mesh>>)>::query().filter(!component::<Instanced>());
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; let mut should_load_mesh = current_mesh_id == None;
if let Some(current) = current_mesh_id { if let Some(current) = current_mesh_id {
should_load_mesh = 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 // 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); render_pass.draw_indexed(0..current_mesh_index_length, 0, 0..1);
} }

View File

@ -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; use std::ops::Range;
pub trait Renderer { pub trait Renderer {
@ -19,5 +19,5 @@ pub trait RenderPass {
fn set_index_buffer(&mut self, name: &str, offset: u64); fn set_index_buffer(&mut self, name: &str, offset: u64);
fn set_vertex_buffer(&mut self, start_slot: u32, name: &str, offset: u64); fn set_vertex_buffer(&mut self, start_slot: u32, name: &str, offset: u64);
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>); fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn setup_bind_groups(&mut self, shader_uniforms: &ShaderUniforms); fn setup_bind_groups(&mut self, entity: Option<&Entity>);
} }

View File

@ -6,7 +6,6 @@ use crate::{
math::Vec4, math::Vec4,
render::render_graph_2::{BindType, UniformPropertyType}, render::render_graph_2::{BindType, UniformPropertyType},
}; };
use std::collections::HashMap;
use legion::storage::Component; use legion::storage::Component;
use zerocopy::AsBytes; use zerocopy::AsBytes;
@ -14,14 +13,12 @@ pub type ShaderUniformSelector = fn(Entity, &World) -> Option<RefMap<&dyn AsUnif
pub struct ShaderUniforms { pub struct ShaderUniforms {
// used for distinguishing // used for distinguishing
pub uniform_selectors: Vec<ShaderUniformSelector>, pub uniform_selectors: Vec<ShaderUniformSelector>,
pub dynamic_uniform_indices: HashMap<String, u64>,
} }
impl ShaderUniforms { impl ShaderUniforms {
pub fn new() -> Self { pub fn new() -> Self {
ShaderUniforms { ShaderUniforms {
uniform_selectors: Vec::new(), uniform_selectors: Vec::new(),
dynamic_uniform_indices: HashMap::new(),
} }
} }

View File

@ -13,6 +13,14 @@ use std::{
ops::Deref, ops::Deref,
}; };
pub struct DynamicUniformBufferInfo {
pub indices: HashMap<usize, Entity>,
pub offsets: HashMap<Entity, u64>,
pub capacity: u64,
pub count: u64,
pub size: u64,
}
pub struct WgpuRenderer { pub struct WgpuRenderer {
pub device: wgpu::Device, pub device: wgpu::Device,
pub queue: wgpu::Queue, pub queue: wgpu::Queue,
@ -24,6 +32,7 @@ pub struct WgpuRenderer {
pub resource_info: HashMap<String, ResourceInfo>, pub resource_info: HashMap<String, ResourceInfo>,
pub bind_groups: HashMap<u64, BindGroupInfo>, pub bind_groups: HashMap<u64, BindGroupInfo>,
pub bind_group_layouts: HashMap<u64, wgpu::BindGroupLayout>, pub bind_group_layouts: HashMap<u64, wgpu::BindGroupLayout>,
pub dynamic_uniform_buffer_info: HashMap<String, DynamicUniformBufferInfo>,
} }
impl WgpuRenderer { impl WgpuRenderer {
@ -62,6 +71,7 @@ impl WgpuRenderer {
resource_info: HashMap::new(), resource_info: HashMap::new(),
bind_groups: HashMap::new(), bind_groups: HashMap::new(),
bind_group_layouts: HashMap::new(), bind_group_layouts: HashMap::new(),
dynamic_uniform_buffer_info: HashMap::new(),
} }
} }
@ -268,7 +278,11 @@ impl WgpuRenderer {
dynamic: _, dynamic: _,
properties: _, 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(); let buffer = self.buffers.get(&b.name).unwrap();
wgpu::BindingResource::Buffer { wgpu::BindingResource::Buffer {
buffer: buffer, buffer: buffer,
@ -303,9 +317,12 @@ impl WgpuRenderer {
bind_group_id bind_group_id
} }
fn setup_dynamic_entity_shader_uniforms(&mut self, world: &World, render_graph: &RenderGraph, encoder: &mut wgpu::CommandEncoder) { fn setup_dynamic_entity_shader_uniforms(
let mut dynamic_uniform_info = HashMap::new(); &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 // 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: // TODO: this breaks down in multiple ways:
// (1) resource_info will be set after the first run so this won't update. // (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 bind_group in pipeline.pipeline_layout.bind_groups.iter() {
for binding in bind_group.bindings.iter() { for binding in bind_group.bindings.iter() {
// if let None = self.resource_info.get(&binding.name) { // if let None = self.resource_info.get(&binding.name) {
if let BindType::Uniform { dynamic: true, .. } = &binding.bind_type { if let BindType::Uniform { dynamic: true, .. } = &binding.bind_type {
if dynamic_uniform_info.contains_key(&binding.name) { if self.dynamic_uniform_buffer_info.contains_key(&binding.name) {
continue; continue;
}
dynamic_uniform_info.insert(binding.name.to_string(), UniformInfo {
size: binding.bind_type.get_uniform_size().unwrap(),
count: 0,
});
} }
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 // count the number of entities providing each uniform
for (name, info) in dynamic_uniform_info.iter_mut() { for (name, info) in self.dynamic_uniform_buffer_info.iter_mut() {
info.count = 0;
for (entity, shader_uniforms) in <Read<ShaderUniforms>>::query().iter_entities(world) { for (entity, shader_uniforms) in <Read<ShaderUniforms>>::query().iter_entities(world) {
if let Some(_) = shader_uniforms.get_uniform_info(world, entity, name) { if let Some(_) = shader_uniforms.get_uniform_info(world, entity, name) {
info.count += 1; info.count += 1;
// TODO: assign indices to shader_uniforms here
} }
} }
} }
// allocate uniform buffers // allocate uniform buffers
for (name, info) in dynamic_uniform_info.iter() { for (name, info) in self.dynamic_uniform_buffer_info.iter_mut() {
let size = wgpu::BIND_BUFFER_ALIGNMENT * info.count; if self.buffers.contains_key(name) && info.count < info.capacity {
if self.buffers.contains_key(name) {
continue; 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 // 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 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 alignment = wgpu::BIND_BUFFER_ALIGNMENT as usize;
let mut offset = 0usize; let mut offset = 0usize;
for (entity, shader_uniforms) in <Read<ShaderUniforms>>::query().iter_entities(world) {
for (i, (entity, shader_uniforms)) in <Read<ShaderUniforms>>::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) { if let Some(bytes) = shader_uniforms.get_uniform_bytes(world, entity, name) {
mapped.data[offset..(offset + bytes.len())].copy_from_slice(bytes.as_slice()); mapped.data[offset..(offset + bytes.len())].copy_from_slice(bytes.as_slice());
offset += alignment; offset += alignment;
@ -369,11 +424,6 @@ impl WgpuRenderer {
} }
} }
pub struct UniformInfo {
pub size: u64,
pub count: u64,
}
impl Renderer for WgpuRenderer { impl Renderer for WgpuRenderer {
fn initialize(&mut self, world: &mut World, render_graph: &mut RenderGraph) { fn initialize(&mut self, world: &mut World, render_graph: &mut RenderGraph) {
let (surface, window_size) = { let (surface, window_size) = {
@ -423,7 +473,7 @@ impl Renderer for WgpuRenderer {
let mut encoder = self let mut encoder = self
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });
self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder); self.setup_dynamic_entity_shader_uniforms(world, render_graph, &mut encoder);
// setup, pipelines, bind groups, and resources // 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); // 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 for (i, bind_group) in self
.pipeline_descriptor .pipeline_descriptor
.pipeline_layout .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(); let mut dynamic_uniform_indices = Vec::new();
for binding in bind_group.bindings.iter() { 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 { if !dynamic {
continue; 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); dynamic_uniform_indices.push(*index);
} }
} }
} }
self.render_pass self.render_pass.set_bind_group(
.set_bind_group(i as u32, &bind_group_info.bind_group, dynamic_uniform_indices.as_slice()); i as u32,
&bind_group_info.bind_group,
dynamic_uniform_indices.as_slice(),
);
} }
} }
} }