refactor render resource assignments

This commit is contained in:
Carter Anderson 2020-03-25 17:31:59 -07:00
parent e523dc92d8
commit 64cd924413
17 changed files with 249 additions and 224 deletions

View file

@ -20,7 +20,7 @@ rand = "0.7.2"
gltf = "0.14.0"
serde = { version = "1", features = ["derive"]}
serde_json = "1.0"
uuid = { version = "0.8", features = ["v4"] }
uuid = { version = "0.8", features = ["v4", "serde"] }
erased-serde = "0.3"
type-uuid = "0.1"
shaderc = "0.6"

View file

@ -16,8 +16,7 @@ use bevy_transform::{prelude::LocalToWorld, transform_system_bundle};
use pipeline::PipelineDescriptor;
use render_graph::{RenderGraph, RenderGraphBuilder};
use render_resource::{
build_entity_render_resource_assignments_system, AssetBatchers,
EntityRenderResourceAssignments, RenderResourceAssignmentsProvider,
build_entity_render_resource_assignments_system, AssetBatchers, EntityRenderResourceAssignments,
};
use shader::Shader;
use std::collections::HashMap;
@ -158,8 +157,6 @@ impl AppBuilder {
}
pub fn add_default_resources(&mut self) -> &mut Self {
let mut asset_batchers = AssetBatchers::default();
asset_batchers.batch_types2::<Mesh, StandardMaterial>();
let resources = self.resources.as_mut().unwrap();
resources.insert(Time::new());
resources.insert(AssetStorage::<Mesh>::new());
@ -169,9 +166,25 @@ impl AppBuilder {
resources.insert(AssetStorage::<PipelineDescriptor>::new());
resources.insert(ShaderPipelineAssignments::new());
resources.insert(CompiledShaderMap::new());
resources.insert(RenderResourceAssignmentsProvider::default());
resources.insert(EntityRenderResourceAssignments::default());
resources.insert(asset_batchers);
self.batch_types2::<Mesh, StandardMaterial>();
self
}
pub fn batch_types2<T1, T2>(&mut self) -> &mut Self
where
T1: 'static,
T2: 'static,
{
{
let resources = self.resources.as_mut().unwrap();
let mut asset_batchers = resources
.get_mut_or_insert_with(|| AssetBatchers::default())
.unwrap();
asset_batchers.batch_types2::<T1, T2>();
}
self
}

View file

@ -66,7 +66,7 @@ impl DrawTarget for AssignedMeshesDrawTarget {
}
// TODO: validate bind group properties against shader uniform properties at least once
render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref());
render_pass.set_render_resource_assignments(Some(&renderable.render_resource_assignments));
render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
}
}
@ -99,7 +99,7 @@ impl DrawTarget for AssignedMeshesDrawTarget {
}
renderer.setup_bind_groups(
renderable.render_resource_assignments.as_ref().unwrap(),
&renderable.render_resource_assignments,
pipeline_descriptor,
);
}

View file

@ -52,7 +52,7 @@ impl DrawTarget for MeshesDrawTarget {
}
// TODO: validate bind group properties against shader uniform properties at least once
render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref());
render_pass.set_render_resource_assignments(Some(&renderable.render_resource_assignments));
render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
}
}

View file

@ -54,7 +54,7 @@ impl DrawTarget for UiDrawTarget {
}
};
render_pass.set_bind_groups(None);
render_pass.set_render_resource_assignments(None);
render_pass.set_index_buffer(self.mesh_index_buffer.unwrap(), 0);
render_pass.set_vertex_buffer(0, self.mesh_vertex_buffer.unwrap(), 0);
render_pass.set_vertex_buffer(1, ui_instances_buffer, 0);

View file

@ -55,8 +55,8 @@ impl<'a> ForwardPipelineBuilder for RenderGraphBuilder<'a> {
},
write_mask: ColorWrite::ALL,
})
.add_draw_target(resource_name::draw_target::ASSIGNED_MESHES)
.add_draw_target(resource_name::draw_target::ASSIGNED_BATCHES);
.add_draw_target(resource_name::draw_target::ASSIGNED_MESHES);
// .add_draw_target(resource_name::draw_target::ASSIGNED_BATCHES);
})
}
}

View file

@ -1,162 +1,7 @@
use super::RenderResourceAssignments;
use crate::asset::{Handle, HandleId, HandleUntyped};
use super::{AssetSetBatcher2, AssetSetBatcherKey2, Batch, BatchKey2};
use crate::asset::{Handle, HandleId};
use legion::prelude::Entity;
use std::{any::TypeId, collections::HashMap, hash::Hash};
// TODO: if/when const generics land, revisit this design
#[derive(Hash, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct BatchKey2 {
pub handle1: HandleId,
pub handle2: HandleId,
}
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct AssetSetBatcherKey2 {
handle1_type: TypeId,
handle2_type: TypeId,
}
struct EntitySetState2 {
handle1: Option<HandleId>,
handle2: Option<HandleId>,
}
impl EntitySetState2 {
fn is_full(&self) -> bool {
self.handle1.is_some() && self.handle2.is_some()
}
}
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Batch {
pub handles: Vec<HandleUntyped>,
pub entity_indices: HashMap<Entity, usize>,
pub current_index: usize,
pub render_resource_assignments: Option<RenderResourceAssignments>,
}
impl Batch {
pub fn add_entity(&mut self, entity: Entity) {
if let None = self.entity_indices.get(&entity) {
self.entity_indices.insert(entity, self.current_index);
self.current_index += 1;
}
}
}
pub struct AssetSetBatcher2 {
key: AssetSetBatcherKey2,
set_batches: HashMap<BatchKey2, Batch>,
entity_set_states: HashMap<Entity, EntitySetState2>,
}
impl AssetSetBatcher2 {
fn new(key: AssetSetBatcherKey2) -> Self {
AssetSetBatcher2 {
key,
set_batches: HashMap::new(),
entity_set_states: HashMap::new(),
}
}
fn add_entity_to_set(&mut self, entity: Entity) {
// these unwraps are safe because this function is only called from set_entity_handle on a "full" state
let state = self.entity_set_states.get(&entity).unwrap();
let key = BatchKey2 {
handle1: state.handle1.unwrap(),
handle2: state.handle2.unwrap(),
};
match self.set_batches.get_mut(&key) {
Some(batch) => {
batch.add_entity(entity);
}
None => {
let mut batch = Batch::default();
batch.handles.push(HandleUntyped {
id: key.handle1,
type_id: self.key.handle1_type,
});
batch.handles.push(HandleUntyped {
id: key.handle2,
type_id: self.key.handle2_type,
});
batch.add_entity(entity);
self.set_batches.insert(key, batch);
}
}
}
pub fn set_entity_handle1(&mut self, entity: Entity, handle_id: HandleId) {
match self.entity_set_states.get_mut(&entity) {
None => {
// TODO: when generalizing to set size 1, ensure you treat set as "full" here
self.entity_set_states.insert(
entity,
EntitySetState2 {
handle1: Some(handle_id),
handle2: None,
},
);
}
Some(state) => {
state.handle1 = Some(handle_id);
if state.is_full() {
self.add_entity_to_set(entity);
}
}
}
}
pub fn set_entity_handle2(&mut self, entity: Entity, handle_id: HandleId) {
match self.entity_set_states.get_mut(&entity) {
None => {
// TODO: when generalizing to set size 1, ensure you treat set as "full" here
self.entity_set_states.insert(
entity,
EntitySetState2 {
handle1: None,
handle2: Some(handle_id),
},
);
}
Some(state) => {
state.handle2 = Some(handle_id);
if state.is_full() {
self.add_entity_to_set(entity);
}
}
}
}
}
impl AssetBatcher for AssetSetBatcher2 {
fn set_entity_handle(&mut self, entity: Entity, handle_type: TypeId, handle_id: HandleId) {
if handle_type == self.key.handle1_type {
self.set_entity_handle1(entity, handle_id);
} else if handle_type == self.key.handle2_type {
self.set_entity_handle2(entity, handle_id);
}
}
fn get_batch2(&self, key: &BatchKey2) -> Option<&Batch> {
self.set_batches.get(key)
}
fn get_batches2(&self) -> std::collections::hash_map::Iter<'_, BatchKey2, Batch> {
self.set_batches.iter()
}
fn get_batches<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Batch> + 'a> {
Box::new(self.set_batches.values())
}
fn get_batches_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Batch> + 'a> {
Box::new(self.set_batches.values_mut())
}
}
use std::{any::TypeId, collections::HashMap};
pub trait AssetBatcher {
fn set_entity_handle(&mut self, entity: Entity, handle_type: TypeId, handle_id: HandleId);

View file

@ -0,0 +1,144 @@
use crate::{
asset::{HandleId, HandleUntyped},
};
use legion::prelude::Entity;
use std::{any::TypeId, collections::HashMap, hash::Hash};
use super::{AssetBatcher, Batch};
// TODO: if/when const generics land, revisit this design in favor of generic array lengths
#[derive(Hash, Eq, PartialEq, Debug, Ord, PartialOrd)]
pub struct BatchKey2 {
pub handle1: HandleId,
pub handle2: HandleId,
}
#[derive(Hash, Eq, PartialEq, Clone, Debug)]
pub struct AssetSetBatcherKey2 {
pub handle1_type: TypeId,
pub handle2_type: TypeId,
}
struct EntitySetState2 {
handle1: Option<HandleId>,
handle2: Option<HandleId>,
}
impl EntitySetState2 {
fn is_full(&self) -> bool {
self.handle1.is_some() && self.handle2.is_some()
}
}
pub struct AssetSetBatcher2 {
key: AssetSetBatcherKey2,
set_batches: HashMap<BatchKey2, Batch>,
entity_set_states: HashMap<Entity, EntitySetState2>,
}
impl AssetSetBatcher2 {
pub fn new(key: AssetSetBatcherKey2) -> Self {
AssetSetBatcher2 {
key,
set_batches: HashMap::new(),
entity_set_states: HashMap::new(),
}
}
pub fn add_entity_to_set(&mut self, entity: Entity) {
// these unwraps are safe because this function is only called from set_entity_handle on a "full" state
let state = self.entity_set_states.get(&entity).unwrap();
let key = BatchKey2 {
handle1: state.handle1.unwrap(),
handle2: state.handle2.unwrap(),
};
match self.set_batches.get_mut(&key) {
Some(batch) => {
batch.add_entity(entity);
}
None => {
let mut batch = Batch::default();
batch.handles.push(HandleUntyped {
id: key.handle1,
type_id: self.key.handle1_type,
});
batch.handles.push(HandleUntyped {
id: key.handle2,
type_id: self.key.handle2_type,
});
batch.add_entity(entity);
self.set_batches.insert(key, batch);
}
}
}
pub fn set_entity_handle1(&mut self, entity: Entity, handle_id: HandleId) {
match self.entity_set_states.get_mut(&entity) {
None => {
// TODO: when generalizing to set size 1, ensure you treat set as "full" here
self.entity_set_states.insert(
entity,
EntitySetState2 {
handle1: Some(handle_id),
handle2: None,
},
);
}
Some(state) => {
state.handle1 = Some(handle_id);
if state.is_full() {
self.add_entity_to_set(entity);
}
}
}
}
pub fn set_entity_handle2(&mut self, entity: Entity, handle_id: HandleId) {
match self.entity_set_states.get_mut(&entity) {
None => {
// TODO: when generalizing to set size 1, ensure you treat set as "full" here
self.entity_set_states.insert(
entity,
EntitySetState2 {
handle1: None,
handle2: Some(handle_id),
},
);
}
Some(state) => {
state.handle2 = Some(handle_id);
if state.is_full() {
self.add_entity_to_set(entity);
}
}
}
}
}
impl AssetBatcher for AssetSetBatcher2 {
fn set_entity_handle(&mut self, entity: Entity, handle_type: TypeId, handle_id: HandleId) {
if handle_type == self.key.handle1_type {
self.set_entity_handle1(entity, handle_id);
} else if handle_type == self.key.handle2_type {
self.set_entity_handle2(entity, handle_id);
}
}
fn get_batch2(&self, key: &BatchKey2) -> Option<&Batch> {
self.set_batches.get(key)
}
fn get_batches2(&self) -> std::collections::hash_map::Iter<'_, BatchKey2, Batch> {
self.set_batches.iter()
}
fn get_batches<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Batch> + 'a> {
Box::new(self.set_batches.values())
}
fn get_batches_mut<'a>(&'a mut self) -> Box<dyn Iterator<Item = &'a mut Batch> + 'a> {
Box::new(self.set_batches.values_mut())
}
}

View file

@ -0,0 +1,25 @@
use crate::{asset::HandleUntyped, render::render_resource::RenderResourceAssignments};
use legion::prelude::Entity;
use std::collections::{HashSet, HashMap};
#[derive(PartialEq, Eq, Debug, Default)]
pub struct Batch {
pub handles: Vec<HandleUntyped>,
pub entities: HashSet<Entity>,
pub instanced_entity_indices: HashMap<Entity, usize>,
pub current_instanced_entity_index: usize,
pub render_resource_assignments: RenderResourceAssignments,
}
impl Batch {
pub fn add_entity(&mut self, entity: Entity) {
self.entities.insert(entity);
}
pub fn add_instanced_entity(&mut self, entity: Entity) {
if let None = self.instanced_entity_indices.get(&entity) {
self.instanced_entity_indices.insert(entity, self.current_instanced_entity_index);
self.current_instanced_entity_index += 1;
}
}
}

View file

@ -0,0 +1,7 @@
mod asset_batcher;
mod asset_batcher2;
mod batch;
pub use asset_batcher::*;
pub use asset_batcher2::*;
pub use batch::*;

View file

@ -1,4 +1,4 @@
use super::{RenderResourceAssignmentsId, RenderResourceAssignmentsProvider};
use super::RenderResourceAssignmentsId;
use crate::prelude::Renderable;
use legion::prelude::*;
use std::collections::HashMap;
@ -18,20 +18,14 @@ impl EntityRenderResourceAssignments {
}
}
// TODO: make sure this runs right before rendering
pub fn build_entity_render_resource_assignments_system() -> Box<dyn Schedulable> {
SystemBuilder::new("EntityRenderResourceAssignments")
.write_resource::<EntityRenderResourceAssignments>()
.write_resource::<RenderResourceAssignmentsProvider>()
.with_query(<Write<Renderable>>::query().filter(changed::<Renderable>()))
.build(|_, world, (entity_assignments, provider), query| {
.build(|_, world, entity_assignments, query| {
for (entity, mut renderable) in query.iter_entities_mut(world) {
if renderable.is_instanced {
renderable.render_resource_assignments = None;
} else if let None = renderable.render_resource_assignments {
let render_resource_assignments = provider.next();
entity_assignments.set(render_resource_assignments.get_id(), entity);
renderable.render_resource_assignments = Some(render_resource_assignments);
}
entity_assignments.set(renderable.render_resource_assignments.get_id(), entity);
}
})
}

View file

@ -1,4 +1,4 @@
mod asset_batcher;
mod batching;
mod buffer;
mod entity_render_resource_assignments;
mod render_resource;
@ -8,7 +8,7 @@ pub mod resource_name;
mod resource_provider;
pub mod resource_providers;
pub use asset_batcher::*;
pub use batching::*;
pub use buffer::*;
pub use entity_render_resource_assignments::*;
pub use render_resource::*;

View file

@ -1,11 +1,13 @@
use super::RenderResource;
use std::collections::{HashMap, HashSet};
use uuid::Uuid;
// PERF: if the assignments are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost
#[derive(Eq, PartialEq, Debug)]
#[derive(Eq, PartialEq, Debug, Default)]
pub struct RenderResourceAssignments {
id: RenderResourceAssignmentsId,
render_resources: HashMap<String, RenderResource>,
vertex_buffers: HashMap<String, (RenderResource, Option<RenderResource>)>,
pub(crate) shader_defs: HashSet<String>,
// TODO: move offsets here to reduce hashing costs?
// render_resource_offsets: HashMap<String, >,
@ -20,28 +22,24 @@ impl RenderResourceAssignments {
self.render_resources.insert(name.to_string(), resource);
}
pub fn get_vertex_buffer(&self, name: &str) -> Option<(RenderResource, Option<RenderResource>)> {
self.vertex_buffers.get(name).cloned()
}
pub fn set_vertex_buffer(&mut self, name: &str, vertices_resource: RenderResource, indices_resource: Option<RenderResource>) {
self.vertex_buffers.insert(name.to_string(), (vertices_resource, indices_resource));
}
pub fn get_id(&self) -> RenderResourceAssignmentsId {
self.id
}
}
#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
pub struct RenderResourceAssignmentsId(usize);
pub struct RenderResourceAssignmentsId(Uuid);
#[derive(Default)]
pub struct RenderResourceAssignmentsProvider {
pub current_id: usize,
}
impl RenderResourceAssignmentsProvider {
pub fn next(&mut self) -> RenderResourceAssignments {
let assignments = RenderResourceAssignments {
id: RenderResourceAssignmentsId(self.current_id),
render_resources: HashMap::new(),
shader_defs: HashSet::new(),
};
self.current_id += 1;
assignments
impl Default for RenderResourceAssignmentsId {
fn default() -> Self {
RenderResourceAssignmentsId(Uuid::new_v4())
}
}

View file

@ -4,7 +4,7 @@ use crate::{
render_graph::RenderGraph,
render_resource::{
AssetBatchers, BufferArrayInfo, BufferInfo, BufferUsage, RenderResource,
RenderResourceAssignments, RenderResourceAssignmentsProvider, ResourceInfo,
RenderResourceAssignments, ResourceInfo,
ResourceProvider,
},
renderer::Renderer,
@ -51,6 +51,7 @@ type UniformHandleQuery<T> = Query<
>,
>;
// TODO: rename to RenderResourceProvider
pub struct UniformResourceProvider<T>
where
T: AsUniforms + Send + Sync + 'static,
@ -123,7 +124,7 @@ where
Self::update_shader_defs(
&uniforms,
renderable.render_resource_assignments.as_mut().unwrap(),
&mut renderable.render_resource_assignments,
);
}
@ -142,7 +143,6 @@ where
if renderable.is_instanced {
if self.is_instanceable {
asset_batchers.set_entity_handle(entity, *handle);
self.increment_instance_count(|| {
let uniforms = assets.get(&handle).unwrap();
Self::get_instance_size(uniforms)
@ -154,12 +154,13 @@ where
);
}
} else {
asset_batchers.set_entity_handle(entity, *handle);
let uniforms = assets
.get(&handle)
.expect("Handle points to a non-existent resource");
Self::update_shader_defs(
uniforms,
renderable.render_resource_assignments.as_mut().unwrap(),
&mut renderable.render_resource_assignments,
);
self.increment_uniform_counts(&uniforms);
@ -391,7 +392,7 @@ where
&uniforms,
renderer,
resources,
renderable.render_resource_assignments.as_mut().unwrap(),
&mut renderable.render_resource_assignments,
staging_buffer,
)
}
@ -422,7 +423,7 @@ where
&uniforms,
renderer,
resources,
renderable.render_resource_assignments.as_mut().unwrap(),
&mut renderable.render_resource_assignments,
staging_buffer,
)
}
@ -443,9 +444,6 @@ where
// all members of the batch until "UniformResourceProvider.update" has run for all members of the batch
if let Some(asset_storage) = resources.get::<AssetStorage<T>>() {
let mut asset_batchers = resources.get_mut::<AssetBatchers>().unwrap();
let mut render_resource_assignments_provider = resources
.get_mut::<RenderResourceAssignmentsProvider>()
.unwrap();
let handle_type = std::any::TypeId::of::<T>();
for batch in asset_batchers.get_handle_batches_mut::<T>().unwrap() {
let handle: Handle<T> = batch
@ -455,19 +453,16 @@ where
.map(|h| (*h).into())
.unwrap();
let render_resource_assignments = batch
.render_resource_assignments
.get_or_insert_with(|| render_resource_assignments_provider.next());
if let Some(uniforms) = asset_storage.get(&handle) {
self.setup_uniform_resources(
uniforms,
renderer,
resources,
render_resource_assignments,
&mut batch.render_resource_assignments,
staging_buffer,
);
Self::update_shader_defs(&uniforms, render_resource_assignments);
Self::update_shader_defs(&uniforms, &mut batch.render_resource_assignments);
}
}
}
@ -643,7 +638,8 @@ where
},
);
self.copy_staging_buffer_to_final_buffers(renderer, staging_buffer)
self.copy_staging_buffer_to_final_buffers(renderer, staging_buffer);
renderer.remove_buffer(staging_buffer);
}
}
}

View file

@ -16,7 +16,7 @@ pub struct Renderable {
// TODO: make these hidden if possible
pub pipelines: Vec<Handle<PipelineDescriptor>>,
pub render_resource_assignments: Option<RenderResourceAssignments>,
pub render_resource_assignments: RenderResourceAssignments,
}
impl Renderable {
@ -35,7 +35,7 @@ impl Default for Renderable {
pipelines: vec![
Handle::new(0), // TODO: this could be better
],
render_resource_assignments: None,
render_resource_assignments: RenderResourceAssignments::default(),
is_instanced: false,
}
}
@ -232,14 +232,12 @@ pub fn update_shader_assignments(world: &mut World, resources: &mut Resources) {
&mut pipeline_descriptor_storage,
&mut shader_storage,
&renderable.pipelines,
renderable.render_resource_assignments.as_ref().unwrap(),
&renderable.render_resource_assignments,
);
// reset shader_defs so they can be changed next frame
renderable
.render_resource_assignments
.as_mut()
.unwrap()
.shader_defs
.clear();
}

View file

@ -55,5 +55,5 @@ pub trait RenderPass {
fn set_index_buffer(&mut self, resource: RenderResource, offset: u64);
fn set_vertex_buffer(&mut self, start_slot: u32, resource: RenderResource, offset: u64);
fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>);
fn set_bind_groups(&mut self, render_resource_assignments: Option<&RenderResourceAssignments>);
fn set_render_resource_assignments(&mut self, render_resource_assignments: Option<&RenderResourceAssignments>) -> Option<Range<u32>>;
}

View file

@ -38,7 +38,10 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
.draw_indexed(indices, base_vertex, instances);
}
fn set_bind_groups(&mut self, render_resource_assignments: Option<&RenderResourceAssignments>) {
fn set_render_resource_assignments(
&mut self,
render_resource_assignments: Option<&RenderResourceAssignments>,
) -> Option<Range<u32>> {
let pipeline_layout = self.pipeline_descriptor.get_layout().unwrap();
for bind_group in pipeline_layout.bind_groups.iter() {
let bind_group_id = bind_group.get_hash().unwrap();
@ -96,5 +99,7 @@ impl<'a, 'b, 'c, 'd> RenderPass for WgpuRenderPass<'a, 'b, 'c, 'd> {
dynamic_uniform_indices.as_slice(),
);
}
None
}
}