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::<LocalToWorld>,
],
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::<StandardMaterial>,
uniform_selector::<LocalToWorld>,
],
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::<StandardMaterial>,
uniform_selector::<LocalToWorld>,
],
dynamic_uniform_indices: indices_3,
},
local_to_world: LocalToWorld::identity(),
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 mesh_query =
<(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;
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);
}

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;
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<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,
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<RefMap<&dyn AsUnif
pub struct ShaderUniforms {
// used for distinguishing
pub uniform_selectors: Vec<ShaderUniformSelector>,
pub dynamic_uniform_indices: HashMap<String, u64>,
}
impl ShaderUniforms {
pub fn new() -> Self {
ShaderUniforms {
uniform_selectors: Vec::new(),
dynamic_uniform_indices: HashMap::new(),
}
}

View file

@ -13,6 +13,14 @@ use std::{
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 device: wgpu::Device,
pub queue: wgpu::Queue,
@ -24,6 +32,7 @@ pub struct WgpuRenderer {
pub resource_info: HashMap<String, ResourceInfo>,
pub bind_groups: HashMap<u64, BindGroupInfo>,
pub bind_group_layouts: HashMap<u64, wgpu::BindGroupLayout>,
pub dynamic_uniform_buffer_info: HashMap<String, DynamicUniformBufferInfo>,
}
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 <Read<ShaderUniforms>>::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 <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) {
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(),
);
}
}
}