fix uniform providers with different field sets

This commit is contained in:
Carter Anderson 2020-03-02 00:19:28 -08:00
parent 5154320f70
commit ca563ea1b3
5 changed files with 41 additions and 27 deletions

View file

@ -1,4 +1,4 @@
use bevy::prelude::*; use bevy::{asset, prelude::*};
use rand::{rngs::StdRng, Rng, SeedableRng}; use rand::{rngs::StdRng, Rng, SeedableRng};
use std::collections::VecDeque; use std::collections::VecDeque;
@ -69,6 +69,11 @@ fn setup(world: &mut World) {
let mut mesh_storage = world.resources.get_mut::<AssetStorage<Mesh>>().unwrap(); let mut mesh_storage = world.resources.get_mut::<AssetStorage<Mesh>>().unwrap();
(mesh_storage.add(cube), mesh_storage.add(plane)) (mesh_storage.add(cube), mesh_storage.add(plane))
}; };
// let texture_handle = {
// let mut texture_storage = world.resources.get_mut::<AssetStorage<Texture>>().unwrap();
// let texture = Texture::load(TextureType::Data(asset::create_texels(256), 256, 256));
// texture_storage.add(texture)
// };
let mut builder = world let mut builder = world
.build() .build()
@ -123,6 +128,7 @@ fn setup(world: &mut World) {
builder = builder.add_archetype(MeshEntity { builder = builder.add_archetype(MeshEntity {
mesh: cube_handle, mesh: cube_handle,
material: StandardMaterial { material: StandardMaterial {
// albedo: texture_handle.into(),
albedo: math::vec4( albedo: math::vec4(
rng.gen_range(0.0, 1.0), rng.gen_range(0.0, 1.0),
rng.gen_range(0.0, 1.0), rng.gen_range(0.0, 1.0),

View file

@ -24,9 +24,17 @@ fn setup(world: &mut World) {
material: StandardMaterial { material: StandardMaterial {
albedo: texture_handle.into(), albedo: texture_handle.into(),
}, },
translation: Translation::new(0.0, 0.0, 1.0), translation: Translation::new(0.0, 0.0, 0.0),
..Default::default() ..Default::default()
}) })
// .add_archetype(MeshEntity {
// mesh: cube_handle,
// material: StandardMaterial {
// albedo: Vec4::new(1.0, 0.0, 0.0, 1.0).into(),
// },
// translation: Translation::new(2.0, 0.0, 0.0),
// ..Default::default()
// })
// light // light
.add_archetype(LightEntity { .add_archetype(LightEntity {
translation: Translation::new(4.0, -4.0, 5.0), translation: Translation::new(4.0, -4.0, 5.0),

View file

@ -429,6 +429,7 @@ impl WgpuRenderer {
} }
pub fn create_entity_bind_group(&mut self, bind_group: &BindGroup, entity: Entity) { pub fn create_entity_bind_group(&mut self, bind_group: &BindGroup, entity: Entity) {
// TODO: don't make this per-entity. bind groups should be re-used across the same resource when possible
let bind_group_id = bind_group.get_hash().unwrap(); let bind_group_id = bind_group.get_hash().unwrap();
let bindings = bind_group.bindings.iter().map(|binding| { let bindings = bind_group.bindings.iter().map(|binding| {
if let Some(resource) = self.get_entity_uniform_resource(entity, &binding.name) { if let Some(resource) = self.get_entity_uniform_resource(entity, &binding.name) {

View file

@ -6,14 +6,15 @@ use crate::{
}, },
}; };
use legion::prelude::*; use legion::prelude::*;
use std::{marker::PhantomData, ops::Deref}; use std::{marker::PhantomData, ops::Deref, collections::{HashSet, HashMap}};
pub struct UniformResourceProvider<T> pub struct UniformResourceProvider<T>
where where
T: AsUniforms + Send + Sync, T: AsUniforms + Send + Sync,
{ {
_marker: PhantomData<T>, _marker: PhantomData<T>,
uniform_buffer_info_resources: Vec<(String, Option<RenderResource>)>, // PERF: somehow remove this HashSet
uniform_buffer_info_resources: HashMap<String, (Option<RenderResource>, usize, HashSet<Entity>)>,
} }
impl<T> UniformResourceProvider<T> impl<T> UniformResourceProvider<T>
@ -22,7 +23,7 @@ where
{ {
pub fn new() -> Self { pub fn new() -> Self {
UniformResourceProvider { UniformResourceProvider {
uniform_buffer_info_resources: Vec::new(), uniform_buffer_info_resources: HashMap::new(),
_marker: PhantomData, _marker: PhantomData,
} }
} }
@ -43,32 +44,27 @@ where
// (2) if we create new buffers, the old bind groups will be invalid // (2) if we create new buffers, the old bind groups will be invalid
// reset all uniform buffer info counts // reset all uniform buffer info counts
for (_name, resource) in self.uniform_buffer_info_resources.iter() { for (_name, (resource, _count, entities)) in self.uniform_buffer_info_resources.iter() {
renderer renderer
.get_dynamic_uniform_buffer_info_mut(resource.unwrap()) .get_dynamic_uniform_buffer_info_mut(resource.unwrap())
.unwrap() .unwrap()
.count = 0; .count = 0;
} }
let mut counts = Vec::new();
for (entity, (uniforms, _renderable)) in query.iter_entities(world) { for (entity, (uniforms, _renderable)) in query.iter_entities(world) {
let mut uniform_index = 0;
let field_uniform_names = uniforms.get_field_uniform_names(); let field_uniform_names = uniforms.get_field_uniform_names();
for uniform_info in UniformInfoIter::new(field_uniform_names, uniforms.deref()) { for uniform_info in UniformInfoIter::new(field_uniform_names, uniforms.deref()) {
match uniform_info.bind_type { match uniform_info.bind_type {
BindType::Uniform { .. } => { BindType::Uniform { .. } => {
// only add the first time a uniform info is processed // only add the first time a uniform info is processed
if self.uniform_buffer_info_resources.len() <= uniform_index { if let None = self.uniform_buffer_info_resources.get(uniform_info.name) {
self.uniform_buffer_info_resources self.uniform_buffer_info_resources
.push((uniform_info.name.to_string(), None)); .insert(uniform_info.name.to_string(), (None, 0, HashSet::new()));
} }
if counts.len() <= uniform_index { let (resource, counts, entities) = self.uniform_buffer_info_resources.get_mut(uniform_info.name).unwrap();
counts.push(0); entities.insert(entity);
} *counts += 1;
counts[uniform_index] += 1;
uniform_index += 1;
} }
BindType::SampledTexture { .. } => { BindType::SampledTexture { .. } => {
let texture_handle = let texture_handle =
@ -115,17 +111,18 @@ where
} }
// allocate uniform buffers // allocate uniform buffers
for (i, (name, resource)) in self.uniform_buffer_info_resources.iter_mut().enumerate() { for (name, (resource, count, entities)) in self.uniform_buffer_info_resources.iter_mut() {
let count = *count as u64;
if let Some(resource) = resource { if let Some(resource) = resource {
let mut info = renderer let mut info = renderer
.get_dynamic_uniform_buffer_info_mut(*resource) .get_dynamic_uniform_buffer_info_mut(*resource)
.unwrap(); .unwrap();
info.count = counts[i]; info.count = count;
continue; continue;
} }
// allocate enough space for twice as many entities as there are currently; // allocate enough space for twice as many entities as there are currently;
let capacity = counts[i] * 2; let capacity = count * 2;
let size = wgpu::BIND_BUFFER_ALIGNMENT * capacity; let size = wgpu::BIND_BUFFER_ALIGNMENT * capacity;
let created_resource = renderer.create_buffer( let created_resource = renderer.create_buffer(
size, size,
@ -133,7 +130,7 @@ where
); );
let mut info = DynamicUniformBufferInfo::new(); let mut info = DynamicUniformBufferInfo::new();
info.count = counts[i]; info.count = count;
info.capacity = capacity; info.capacity = capacity;
renderer.add_dynamic_uniform_buffer_info(created_resource, info); renderer.add_dynamic_uniform_buffer_info(created_resource, info);
*resource = Some(created_resource); *resource = Some(created_resource);
@ -141,7 +138,7 @@ where
} }
// copy entity uniform data to buffers // copy entity uniform data to buffers
for (name, resource) in self.uniform_buffer_info_resources.iter() { for (name, (resource, _count, entities)) in self.uniform_buffer_info_resources.iter() {
let resource = resource.unwrap(); let resource = resource.unwrap();
let size = { let size = {
let info = renderer.get_dynamic_uniform_buffer_info(resource).unwrap(); let info = renderer.get_dynamic_uniform_buffer_info(resource).unwrap();
@ -153,24 +150,28 @@ where
let info = renderer let info = renderer
.get_dynamic_uniform_buffer_info_mut(resource) .get_dynamic_uniform_buffer_info_mut(resource)
.unwrap(); .unwrap();
for (i, (entity, _)) in query.iter_entities(world).enumerate() { for (entity, _) in query.iter_entities(world) {
if !entities.contains(&entity) {
continue;
}
// TODO: check if index has changed. if it has, then entity should be updated // 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: only mem-map entities if their data has changed
// PERF: These hashmap inserts are pretty expensive (10 fps for 10000 entities) // PERF: These hashmap inserts are pretty expensive (10 fps for 10000 entities)
info.offsets.insert(entity, offset as u64); info.offsets.insert(entity, offset as u64);
info.indices.insert(i, entity);
// TODO: try getting ref first // TODO: try getting ref first
offset += alignment; offset += alignment;
} }
// let mut data = vec![Default::default(); size as usize];
let mapped_buffer_resource = renderer.create_buffer_mapped( let mapped_buffer_resource = renderer.create_buffer_mapped(
size as usize, size as usize,
wgpu::BufferUsage::COPY_SRC, wgpu::BufferUsage::COPY_SRC,
&mut |mapped| { &mut |mapped| {
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 (uniforms, _renderable) in query.iter(world) { for (entity, (uniforms, _renderable)) in query.iter_entities(world) {
if !entities.contains(&entity) {
continue;
}
// TODO: check if index has changed. if it has, then entity should be updated // 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: only mem-map entities if their data has changed
// TODO: try getting bytes ref first // TODO: try getting bytes ref first

View file

@ -171,7 +171,6 @@ pub struct UniformInfo<'a> {
} }
pub struct DynamicUniformBufferInfo { pub struct DynamicUniformBufferInfo {
pub indices: HashMap<usize, Entity>,
pub offsets: HashMap<Entity, u64>, pub offsets: HashMap<Entity, u64>,
pub capacity: u64, pub capacity: u64,
pub count: u64, pub count: u64,
@ -182,7 +181,6 @@ impl DynamicUniformBufferInfo {
DynamicUniformBufferInfo { DynamicUniformBufferInfo {
capacity: 0, capacity: 0,
count: 0, count: 0,
indices: HashMap::new(),
offsets: HashMap::new(), offsets: HashMap::new(),
} }
} }