diff --git a/bevy_derive/src/lib.rs b/bevy_derive/src/lib.rs index 14b1a85322..fc7c635557 100644 --- a/bevy_derive/src/lib.rs +++ b/bevy_derive/src/lib.rs @@ -70,6 +70,8 @@ pub fn derive_entity_archetype(input: TokenStream) -> TokenStream { } }) } + +// TODO: ensure shader_def and instance/vertex are mutually exclusive #[derive(FromMeta, Debug, Default)] struct UniformAttributeArgs { #[darling(default)] @@ -329,6 +331,7 @@ pub fn derive_uniforms(input: TokenStream) -> TokenStream { } } + // TODO: move this to field_info and add has_shader_def(&self, &str) -> bool // TODO: this will be very allocation heavy. find a way to either make this allocation free // or alternatively only run it when the shader_defs have changed fn get_shader_defs(&self) -> Option> { diff --git a/src/app/app.rs b/src/app/app.rs index 6184e81232..2ddb9117d5 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -65,12 +65,6 @@ impl App { self.resources.insert(window); - log::info!("Initializing the example..."); - - if let Some(ref mut renderer) = self.renderer { - renderer.initialize(&mut self.world, &mut self.resources); - } - log::info!("Entering render loop..."); event_loop.run(move |event, _, control_flow| { *control_flow = if cfg!(feature = "metal-auto-capture") { diff --git a/src/app/app_builder.rs b/src/app/app_builder.rs index e97abfdf50..5b17de5f39 100644 --- a/src/app/app_builder.rs +++ b/src/app/app_builder.rs @@ -16,7 +16,8 @@ use bevy_transform::{prelude::LocalToWorld, transform_system_bundle}; use pipeline::PipelineDescriptor; use render_graph::{RenderGraph, RenderGraphBuilder}; use render_resource::{ - AssetBatchers, EntityRenderResourceAssignments, RenderResourceAssignmentsProvider, + build_entity_render_resource_assignments_system, AssetBatchers, + EntityRenderResourceAssignments, RenderResourceAssignmentsProvider, }; use shader::Shader; use std::collections::HashMap; @@ -175,6 +176,7 @@ impl AppBuilder { } pub fn add_default_systems(&mut self) -> &mut Self { + self.add_system(build_entity_render_resource_assignments_system()); self.add_system(ui::ui_update_system::build_ui_update_system()); for transform_system in transform_system_bundle::build(self.world.as_mut().unwrap()).drain(..) diff --git a/src/render/draw_target/draw_targets/assigned_meshes_draw_target.rs b/src/render/draw_target/draw_targets/assigned_meshes_draw_target.rs index 7bc6e524d8..388a142aa9 100644 --- a/src/render/draw_target/draw_targets/assigned_meshes_draw_target.rs +++ b/src/render/draw_target/draw_targets/assigned_meshes_draw_target.rs @@ -28,13 +28,16 @@ impl DrawTarget for AssignedMeshesDrawTarget { let mut current_mesh_handle = None; let mut current_mesh_index_len = 0; - let assigned_entities = shader_pipeline_assignments + let assigned_render_resource_assignments = shader_pipeline_assignments .assignments .get(&pipeline_handle); - if let Some(assigned_entities) = assigned_entities { - for entity in assigned_entities.iter() { + if let Some(assigned_render_resource_assignments) = assigned_render_resource_assignments { + for assignment_id in assigned_render_resource_assignments.iter() { // TODO: hopefully legion has better random access apis that are more like queries? + let entity = entity_render_resource_assignments + .get(*assignment_id) + .unwrap(); let renderable = world.get_component::(*entity).unwrap(); if !renderable.is_visible || renderable.is_instanced { continue; @@ -63,8 +66,7 @@ impl DrawTarget for AssignedMeshesDrawTarget { } // TODO: validate bind group properties against shader uniform properties at least once - let render_resource_assignments = entity_render_resource_assignments.get(*entity); - render_pass.set_bind_groups(render_resource_assignments); + render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref()); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1); } } @@ -78,26 +80,28 @@ impl DrawTarget for AssignedMeshesDrawTarget { pipeline_handle: Handle, ) { let shader_pipeline_assignments = resources.get::().unwrap(); - let assigned_entities = shader_pipeline_assignments + let entity_render_resource_assignments = + resources.get::().unwrap(); + let assigned_render_resource_assignments = shader_pipeline_assignments .assignments .get(&pipeline_handle); let pipeline_storage = resources.get::>().unwrap(); - let entity_render_resource_assignments = - resources.get::().unwrap(); let pipeline_descriptor = pipeline_storage.get(&pipeline_handle).unwrap(); - if let Some(assigned_entities) = assigned_entities { - for entity in assigned_entities.iter() { + if let Some(assigned_render_resource_assignments) = assigned_render_resource_assignments { + for assignment_id in assigned_render_resource_assignments.iter() { // TODO: hopefully legion has better random access apis that are more like queries? + let entity = entity_render_resource_assignments + .get(*assignment_id) + .unwrap(); let renderable = world.get_component::(*entity).unwrap(); if !renderable.is_visible || renderable.is_instanced { continue; } - if let Some(render_resource_assignments) = - entity_render_resource_assignments.get(*entity) - { - renderer.setup_bind_groups(render_resource_assignments, pipeline_descriptor); - } + renderer.setup_bind_groups( + renderable.render_resource_assignments.as_ref().unwrap(), + pipeline_descriptor, + ); } } } diff --git a/src/render/draw_target/draw_targets/meshes_draw_target.rs b/src/render/draw_target/draw_targets/meshes_draw_target.rs index 0b5510ff69..eb0c530c70 100644 --- a/src/render/draw_target/draw_targets/meshes_draw_target.rs +++ b/src/render/draw_target/draw_targets/meshes_draw_target.rs @@ -5,7 +5,7 @@ use crate::{ draw_target::DrawTarget, mesh::Mesh, pipeline::PipelineDescriptor, - render_resource::{resource_name, EntityRenderResourceAssignments, ResourceInfo}, + render_resource::{resource_name, ResourceInfo}, renderer::RenderPass, Renderable, }, @@ -18,17 +18,14 @@ impl DrawTarget for MeshesDrawTarget { fn draw( &self, world: &World, - resources: &Resources, + _resources: &Resources, render_pass: &mut dyn RenderPass, _pipeline_handle: Handle, ) { let mut current_mesh_handle = None; let mut current_mesh_index_len = 0; let mesh_query = <(Read>, Read)>::query(); - let entity_render_resource_assignments = - resources.get::().unwrap(); - - for (entity, (mesh, renderable)) in mesh_query.iter_entities(world) { + for (mesh, renderable) in mesh_query.iter(world) { if !renderable.is_visible || renderable.is_instanced { continue; } @@ -55,8 +52,7 @@ impl DrawTarget for MeshesDrawTarget { } // TODO: validate bind group properties against shader uniform properties at least once - let render_resource_assignments = entity_render_resource_assignments.get(entity); - render_pass.set_bind_groups(render_resource_assignments); + render_pass.set_bind_groups(renderable.render_resource_assignments.as_ref()); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1); } } diff --git a/src/render/render_resource/entity_render_resource_assignments.rs b/src/render/render_resource/entity_render_resource_assignments.rs index 63db53dc19..a6d5c4e45c 100644 --- a/src/render/render_resource/entity_render_resource_assignments.rs +++ b/src/render/render_resource/entity_render_resource_assignments.rs @@ -1,22 +1,37 @@ -use super::RenderResourceAssignments; -use legion::prelude::Entity; +use super::{RenderResourceAssignmentsId, RenderResourceAssignmentsProvider}; +use crate::prelude::Renderable; +use legion::prelude::*; use std::collections::HashMap; #[derive(Default)] pub struct EntityRenderResourceAssignments { - entity_assignments: HashMap, + entity_assignments: HashMap, } impl EntityRenderResourceAssignments { - pub fn set(&mut self, entity: Entity, assignments: RenderResourceAssignments) { - self.entity_assignments.insert(entity, assignments); + pub fn set(&mut self, id: RenderResourceAssignmentsId, entity: Entity) { + self.entity_assignments.insert(id, entity); } - pub fn get(&self, entity: Entity) -> Option<&RenderResourceAssignments> { - self.entity_assignments.get(&entity) - } - - pub fn get_mut(&mut self, entity: Entity) -> Option<&mut RenderResourceAssignments> { - self.entity_assignments.get_mut(&entity) + pub fn get(&self, id: RenderResourceAssignmentsId) -> Option<&Entity> { + self.entity_assignments.get(&id) } } + +pub fn build_entity_render_resource_assignments_system() -> Box { + SystemBuilder::new("EntityRenderResourceAssignments") + .write_resource::() + .write_resource::() + .with_query(>::query().filter(changed::())) + .build(|_, world, (entity_assignments, provider), 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); + } + } + }) +} diff --git a/src/render/render_resource/render_resource_assignments.rs b/src/render/render_resource/render_resource_assignments.rs index 9609396354..3341930572 100644 --- a/src/render/render_resource/render_resource_assignments.rs +++ b/src/render/render_resource/render_resource_assignments.rs @@ -1,12 +1,14 @@ use super::RenderResource; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; -// TODO: consider merging this with entity_uniform_resource // 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)] pub struct RenderResourceAssignments { id: RenderResourceAssignmentsId, render_resources: HashMap, + pub(crate) shader_defs: HashSet, + // TODO: move offsets here to reduce hashing costs? + // render_resource_offsets: HashMap, } impl RenderResourceAssignments { @@ -36,6 +38,7 @@ impl RenderResourceAssignmentsProvider { let assignments = RenderResourceAssignments { id: RenderResourceAssignmentsId(self.current_id), render_resources: HashMap::new(), + shader_defs: HashSet::new(), }; self.current_id += 1; diff --git a/src/render/render_resource/resource_providers/uniform_resource_provider.rs b/src/render/render_resource/resource_providers/uniform_resource_provider.rs index 6cbe56fb11..68792a56a9 100644 --- a/src/render/render_resource/resource_providers/uniform_resource_provider.rs +++ b/src/render/render_resource/resource_providers/uniform_resource_provider.rs @@ -5,9 +5,8 @@ use crate::{ render_graph::RenderGraph, render_resource::{ AssetBatchers, BufferArrayInfo, BufferDynamicUniformInfo, BufferInfo, BufferUsage, - EntityRenderResourceAssignments, RenderResource, RenderResourceAssignments, - RenderResourceAssignmentsId, RenderResourceAssignmentsProvider, ResourceInfo, - ResourceProvider, + RenderResource, RenderResourceAssignments, RenderResourceAssignmentsId, + RenderResourceAssignmentsProvider, ResourceInfo, ResourceProvider, }, renderer::Renderer, shader::{AsUniforms, UniformInfoIter}, @@ -39,7 +38,7 @@ where >, asset_resources: HashMap, HashMap>, resource_query: Query< - (Read, Read), + (Read, Write), EntityFilterTuple< And<(ComponentFilter, ComponentFilter)>, And<(Passthrough, Passthrough)>, @@ -48,7 +47,7 @@ where >, handle_query: Option< Query< - (Read>, Read), + (Read>, Write), EntityFilterTuple< And<(ComponentFilter>, ComponentFilter)>, And<(Passthrough, Passthrough)>, @@ -67,46 +66,31 @@ where uniform_buffer_info_resources: Default::default(), asset_resources: Default::default(), _marker: PhantomData, - resource_query: <(Read, Read)>::query(), - handle_query: Some(<(Read>, Read)>::query()), + resource_query: <(Read, Write)>::query(), + handle_query: Some(<(Read>, Write)>::query()), } } fn update_asset_uniforms( &mut self, renderer: &mut dyn Renderer, - world: &World, + world: &mut World, resources: &Resources, ) { let handle_query = self.handle_query.take().unwrap(); let mut asset_batchers = resources.get_mut::().unwrap(); - let mut entity_render_resource_assignments = resources - .get_mut::() - .unwrap(); - let mut render_resource_assignments_provider = resources - .get_mut::() - .unwrap(); // TODO: only update handle values when Asset value has changed if let Some(asset_storage) = resources.get::>() { - for (entity, (handle, renderable)) in handle_query.iter_entities(world) { + for (entity, (handle, mut renderable)) in handle_query.iter_entities_mut(world) { if renderable.is_instanced { asset_batchers.set_entity_handle(entity, *handle); } else { - let render_resource_assignments = if let Some(assignments) = - entity_render_resource_assignments.get_mut(entity) - { - assignments - } else { - entity_render_resource_assignments - .set(entity, render_resource_assignments_provider.next()); - entity_render_resource_assignments.get_mut(entity).unwrap() - }; if let Some(uniforms) = asset_storage.get(&handle) { self.setup_uniform_resources( uniforms, renderer, resources, - render_resource_assignments, + renderable.render_resource_assignments.as_mut().unwrap(), true, Some(*handle), ) @@ -127,8 +111,7 @@ where dynamic_unforms: bool, asset_handle: Option>, ) { - let field_infos = uniforms.get_field_infos(); - for uniform_info in UniformInfoIter::new(field_infos, uniforms.deref()) { + for uniform_info in UniformInfoIter::new(uniforms.deref()) { match uniform_info.bind_type { BindType::Uniform { .. } => { if dynamic_unforms { @@ -273,11 +256,9 @@ where fn setup_dynamic_uniform_buffers( &mut self, renderer: &mut dyn Renderer, - world: &World, + world: &mut World, resources: &Resources, ) { - let entity_render_resource_assignments = - resources.get::().unwrap(); // allocate uniform buffers for (name, (resource, count, _entities)) in self.uniform_buffer_info_resources.iter_mut() { let count = *count as u64; @@ -330,40 +311,40 @@ where // TODO: check if index has changed. if it has, then entity should be updated // TODO: only mem-map entities if their data has changed // PERF: These hashmap inserts are pretty expensive (10 fps for 10000 entities) - for (entity, (_, renderable)) in self.resource_query.iter_entities(world) { + for (_, renderable) in self.resource_query.iter_mut(world) { if renderable.is_instanced { continue; } + let id = renderable + .render_resource_assignments + .as_ref() + .unwrap() + .get_id(); + // this unwrap is safe because the assignments were created in the calling function - let render_resource_assignments = - entity_render_resource_assignments.get(entity).unwrap(); - if !entities.contains(&render_resource_assignments.get_id()) { + if !entities.contains(&id) { continue; } - dynamic_uniform_info - .offsets - .insert(render_resource_assignments.get_id(), offset as u32); + dynamic_uniform_info.offsets.insert(id, offset as u32); offset += alignment; } - for (entity, (_, renderable)) in - self.handle_query.as_ref().unwrap().iter_entities(world) - { + for (_, renderable) in self.handle_query.as_ref().unwrap().iter_mut(world) { if renderable.is_instanced { continue; } - let render_resource_assignments = - entity_render_resource_assignments.get(entity).unwrap(); - if !entities.contains(&render_resource_assignments.get_id()) { - continue; - } - dynamic_uniform_info - .offsets - .insert(render_resource_assignments.get_id(), offset as u32); + dynamic_uniform_info.offsets.insert( + renderable + .render_resource_assignments + .as_ref() + .unwrap() + .get_id(), + offset as u32, + ); offset += alignment; } @@ -379,16 +360,18 @@ where &mut |mapped| { let alignment = BIND_BUFFER_ALIGNMENT as usize; let mut offset = 0usize; - for (entity, (uniforms, renderable)) in - self.resource_query.iter_entities(world) - { + for (uniforms, renderable) in self.resource_query.iter_mut(world) { if renderable.is_instanced { continue; } - let render_resource_assignments = - entity_render_resource_assignments.get(entity).unwrap(); - if !entities.contains(&render_resource_assignments.get_id()) { + if !entities.contains( + &renderable + .render_resource_assignments + .as_ref() + .unwrap() + .get_id(), + ) { continue; } if let Some(uniform_bytes) = uniforms.get_uniform_bytes_ref(&name) { @@ -403,16 +386,20 @@ where } if let Some(asset_storage) = resources.get::>() { - for (entity, (handle, renderable)) in - self.handle_query.as_ref().unwrap().iter_entities(world) + for (handle, renderable) in + self.handle_query.as_ref().unwrap().iter_mut(world) { if renderable.is_instanced { continue; } - let render_resource_assignments = - entity_render_resource_assignments.get(entity).unwrap(); - if !entities.contains(&render_resource_assignments.get_id()) { + if !entities.contains( + &renderable + .render_resource_assignments + .as_ref() + .unwrap() + .get_id(), + ) { continue; } @@ -471,7 +458,7 @@ where // (2) if we create new buffers, the old bind groups will be invalid // reset all uniform buffer info counts - let query = <(Read, Read)>::query(); + let query = <(Read, Write)>::query(); for (_name, (resource, count, _entities)) in self.uniform_buffer_info_resources.iter_mut() { if let Some(ResourceInfo::Buffer(BufferInfo { array_info: Some(array_info), @@ -486,30 +473,16 @@ where self.update_asset_uniforms(renderer, world, resources); - let mut entity_render_resource_assignments = resources - .get_mut::() - .unwrap(); - let mut render_resource_assignments_provider = resources - .get_mut::() - .unwrap(); - for (entity, (uniforms, renderable)) in query.iter_entities(world) { + for (uniforms, mut renderable) in query.iter_mut(world) { if renderable.is_instanced { continue; } - let render_resource_assignments = - if let Some(assignments) = entity_render_resource_assignments.get_mut(entity) { - assignments - } else { - entity_render_resource_assignments - .set(entity, render_resource_assignments_provider.next()); - entity_render_resource_assignments.get_mut(entity).unwrap() - }; self.setup_uniform_resources( &uniforms, renderer, resources, - render_resource_assignments, + renderable.render_resource_assignments.as_mut().unwrap(), true, None, ); @@ -521,7 +494,12 @@ where for (uniforms, mut renderable) in <(Read, Write)>::query().iter_mut(world) { if let Some(shader_defs) = uniforms.get_shader_defs() { for shader_def in shader_defs { - renderable.shader_defs.insert(shader_def); + renderable + .render_resource_assignments + .as_mut() + .unwrap() + .shader_defs + .insert(shader_def); } } } @@ -533,7 +511,12 @@ where let uniforms = asset_storage.get(&handle).unwrap(); if let Some(shader_defs) = uniforms.get_shader_defs() { for shader_def in shader_defs { - renderable.shader_defs.insert(shader_def); + renderable + .render_resource_assignments + .as_mut() + .unwrap() + .shader_defs + .insert(shader_def); } } } diff --git a/src/render/renderable.rs b/src/render/renderable.rs index ac3011f482..c035c10623 100644 --- a/src/render/renderable.rs +++ b/src/render/renderable.rs @@ -1,5 +1,6 @@ use super::{ pipeline::PipelineDescriptor, + render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId}, shader::{Shader, ShaderSource}, }; use crate::{ @@ -12,8 +13,10 @@ use std::collections::{HashMap, HashSet}; pub struct Renderable { pub is_visible: bool, pub is_instanced: bool, + + // TODO: make these hidden if possible pub pipelines: Vec>, - pub shader_defs: HashSet, + pub render_resource_assignments: Option, } impl Renderable { @@ -32,12 +35,13 @@ impl Default for Renderable { pipelines: vec![ Handle::new(0), // TODO: this could be better ], - shader_defs: HashSet::new(), + render_resource_assignments: None, is_instanced: false, } } } +// TODO: consider using (Typeid, fieldinfo.index) in place of string for hashes pub struct CompiledShaderMap { // TODO: need macro hash lookup pub source_to_compiled: HashMap, Vec<(HashSet, Handle)>>, @@ -52,10 +56,102 @@ impl CompiledShaderMap { pipeline_to_macro_pipelines: HashMap::new(), } } + + fn update_shader_assignments( + &mut self, + render_graph: &mut RenderGraph, + shader_pipeline_assignments: &mut ShaderPipelineAssignments, + pipeline_storage: &mut AssetStorage, + shader_storage: &mut AssetStorage, + pipelines: &[Handle], + render_resource_assignments: &RenderResourceAssignments, + ) { + for pipeline_handle in pipelines.iter() { + if let None = self.pipeline_to_macro_pipelines.get(pipeline_handle) { + self.pipeline_to_macro_pipelines + .insert(*pipeline_handle, Vec::new()); + } + + let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = self + .pipeline_to_macro_pipelines + .get_mut(pipeline_handle) + .unwrap() + .iter() + .find(|(shader_defs, _macroed_pipeline_handle)| { + *shader_defs == render_resource_assignments.shader_defs + }) { + *macroed_pipeline_handle + } else { + let pipeline_descriptor = pipeline_storage.get(pipeline_handle).unwrap(); + let macroed_pipeline_handle = { + let mut macroed_vertex_handle = try_compiling_shader_with_macros( + self, + shader_storage, + &render_resource_assignments, + &pipeline_descriptor.shader_stages.vertex, + ); + let mut macroed_fragment_handle = pipeline_descriptor + .shader_stages + .fragment + .as_ref() + .map(|fragment| { + try_compiling_shader_with_macros( + self, + shader_storage, + &render_resource_assignments, + fragment, + ) + }); + + if macroed_vertex_handle.is_some() || macroed_fragment_handle.is_some() { + let mut macroed_pipeline = pipeline_descriptor.clone(); + if let Some(vertex) = macroed_vertex_handle.take() { + macroed_pipeline.shader_stages.vertex = vertex; + } + + if let Some(fragment) = macroed_fragment_handle.take() { + macroed_pipeline.shader_stages.fragment = fragment; + } + + let macroed_pipeline_handle = pipeline_storage.add(macroed_pipeline); + // TODO: get correct pass name + render_graph + .add_pipeline(resource_name::pass::MAIN, macroed_pipeline_handle); + macroed_pipeline_handle + } else { + *pipeline_handle + } + }; + + let macro_pipelines = self + .pipeline_to_macro_pipelines + .get_mut(pipeline_handle) + .unwrap(); + macro_pipelines.push(( + render_resource_assignments.shader_defs.clone(), + macroed_pipeline_handle, + )); + macroed_pipeline_handle + }; + + // TODO: this will break down if pipeline layout changes. fix this with "auto-layout" + if let None = shader_pipeline_assignments.assignments.get(&final_handle) { + shader_pipeline_assignments + .assignments + .insert(final_handle, Vec::new()); + } + + let assignments = shader_pipeline_assignments + .assignments + .get_mut(&final_handle) + .unwrap(); + assignments.push(render_resource_assignments.get_id()); + } + } } pub struct ShaderPipelineAssignments { - pub assignments: HashMap, Vec>, + pub assignments: HashMap, Vec>, } impl ShaderPipelineAssignments { @@ -69,7 +165,7 @@ impl ShaderPipelineAssignments { fn try_compiling_shader_with_macros( compiled_shader_map: &mut CompiledShaderMap, shader_storage: &mut AssetStorage, - renderable: &Renderable, + assignments: &RenderResourceAssignments, shader_handle: &Handle, ) -> Option> { if let None = compiled_shader_map.source_to_compiled.get(shader_handle) { @@ -91,21 +187,22 @@ fn try_compiling_shader_with_macros( if let Some((_shader_defs, compiled_shader)) = compiled_shaders .iter() - .find(|(shader_defs, _shader)| *shader_defs == renderable.shader_defs) + .find(|(shader_defs, _shader)| *shader_defs == assignments.shader_defs) { Some(compiled_shader.clone()) } else { - let shader_def_vec = renderable + let shader_def_vec = assignments .shader_defs .iter() .cloned() .collect::>(); let compiled_shader = shader.get_spirv_shader(Some(&shader_def_vec)); - compiled_shaders.push((renderable.shader_defs.clone(), *shader_handle)); + compiled_shaders.push((assignments.shader_defs.clone(), *shader_handle)); let compiled_shader_handle = shader_storage.add(compiled_shader); Some(compiled_shader_handle) } } + pub fn update_shader_assignments(world: &mut World, resources: &mut Resources) { // PERF: this seems like a lot of work for things that don't change that often. // lots of string + hashset allocations. sees uniform_resource_provider for more context @@ -122,102 +219,29 @@ pub fn update_shader_assignments(world: &mut World, resources: &mut Resources) { // reset assignments so they are updated every frame shader_pipeline_assignments.assignments = HashMap::new(); - for (entity, mut renderable) in >::query().iter_entities_mut(world) { - // if instancing is enabled, set the def here + // TODO: only update when renderable is changed + for mut renderable in >::query().iter_mut(world) { + // skip instanced entities. their batched RenderResourceAssignments will handle shader assignments if renderable.is_instanced { - renderable.shader_defs.insert("INSTANCING".to_string()); + continue; } - for pipeline_handle in renderable.pipelines.iter() { - if let None = compiled_shader_map - .pipeline_to_macro_pipelines - .get(pipeline_handle) - { - compiled_shader_map - .pipeline_to_macro_pipelines - .insert(*pipeline_handle, Vec::new()); - } + compiled_shader_map.update_shader_assignments( + &mut render_graph, + &mut shader_pipeline_assignments, + &mut pipeline_descriptor_storage, + &mut shader_storage, + &renderable.pipelines, + renderable.render_resource_assignments.as_ref().unwrap(), + ); - let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = - compiled_shader_map - .pipeline_to_macro_pipelines - .get_mut(pipeline_handle) - .unwrap() - .iter() - .find(|(shader_defs, _macroed_pipeline_handle)| { - *shader_defs == renderable.shader_defs - }) { - *macroed_pipeline_handle - } else { - let pipeline_descriptor = - pipeline_descriptor_storage.get(pipeline_handle).unwrap(); - let macroed_pipeline_handle = { - let mut macroed_vertex_handle = try_compiling_shader_with_macros( - &mut compiled_shader_map, - &mut shader_storage, - &renderable, - &pipeline_descriptor.shader_stages.vertex, - ); - let mut macroed_fragment_handle = pipeline_descriptor - .shader_stages - .fragment - .as_ref() - .map(|fragment| { - try_compiling_shader_with_macros( - &mut compiled_shader_map, - &mut shader_storage, - &renderable, - fragment, - ) - }); - - if macroed_vertex_handle.is_some() || macroed_fragment_handle.is_some() { - let mut macroed_pipeline = pipeline_descriptor.clone(); - if let Some(vertex) = macroed_vertex_handle.take() { - macroed_pipeline.shader_stages.vertex = vertex; - } - - if let Some(fragment) = macroed_fragment_handle.take() { - macroed_pipeline.shader_stages.fragment = fragment; - } - - let macroed_pipeline_handle = - pipeline_descriptor_storage.add(macroed_pipeline); - // TODO: get correct pass name - render_graph - .add_pipeline(resource_name::pass::MAIN, macroed_pipeline_handle); - macroed_pipeline_handle - } else { - *pipeline_handle - } - }; - - let macro_pipelines = compiled_shader_map - .pipeline_to_macro_pipelines - .get_mut(pipeline_handle) - .unwrap(); - macro_pipelines.push((renderable.shader_defs.clone(), macroed_pipeline_handle)); - macroed_pipeline_handle - }; - - // TODO: this will break down if pipeline layout changes. fix this with "auto-layout" - if let None = shader_pipeline_assignments.assignments.get(&final_handle) { - shader_pipeline_assignments - .assignments - .insert(final_handle, Vec::new()); - } - - let assignments = shader_pipeline_assignments - .assignments - .get_mut(&final_handle) - .unwrap(); - assignments.push(entity); - } + // reset shader_defs so they can be changed next frame + renderable + .render_resource_assignments + .as_mut() + .unwrap() + .shader_defs + .clear(); } } - - // cleanup entity shader_defs so next frame they can be refreshed - for mut renderable in >::query().iter_mut(world) { - renderable.shader_defs = HashSet::new(); - } } diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index b4197ad7ea..c33095a978 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -11,7 +11,6 @@ use crate::{ use std::ops::Range; pub trait Renderer { - fn initialize(&mut self, world: &mut World, resources: &mut Resources); fn resize(&mut self, world: &mut World, resources: &mut Resources); fn update(&mut self, world: &mut World, resources: &mut Resources); fn create_buffer_with_data(&mut self, buffer_info: BufferInfo, data: &[u8]) -> RenderResource; diff --git a/src/render/renderer/renderers/wgpu_renderer/wgpu_renderer.rs b/src/render/renderer/renderers/wgpu_renderer/wgpu_renderer.rs index 7ee74953cb..c9d225728e 100644 --- a/src/render/renderer/renderers/wgpu_renderer/wgpu_renderer.rs +++ b/src/render/renderer/renderers/wgpu_renderer/wgpu_renderer.rs @@ -29,6 +29,7 @@ pub struct WgpuRenderer { pub swap_chain_descriptor: wgpu::SwapChainDescriptor, pub render_pipelines: HashMap, wgpu::RenderPipeline>, pub wgpu_resources: WgpuResources, + pub intialized: bool, } impl WgpuRenderer { @@ -61,12 +62,25 @@ impl WgpuRenderer { queue, surface: None, encoder: None, + intialized: false, swap_chain_descriptor, wgpu_resources: WgpuResources::new(), render_pipelines: HashMap::new(), } } + fn initialize(&mut self, world: &mut World, resources: &mut Resources) { + if self.intialized { + return; + } + + self.create_surface(resources); + self.initialize_resource_providers(world, resources); + self.resize(world, resources); + + self.intialized = true; + } + pub fn setup_vertex_buffer_descriptors( render_graph: &RenderGraph, vertex_spirv: &Shader, @@ -396,12 +410,6 @@ impl WgpuRenderer { } impl Renderer for WgpuRenderer { - fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - self.create_surface(resources); - self.initialize_resource_providers(world, resources); - self.resize(world, resources); - } - fn resize(&mut self, world: &mut World, resources: &mut Resources) { let window_size = { let window = resources.get::().unwrap(); @@ -436,6 +444,7 @@ impl Renderer for WgpuRenderer { } fn update(&mut self, world: &mut World, resources: &mut Resources) { + self.initialize(world, resources); // 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( diff --git a/src/render/shader/uniform.rs b/src/render/shader/uniform.rs index 7403df3d44..e230fc3265 100644 --- a/src/render/shader/uniform.rs +++ b/src/render/shader/uniform.rs @@ -36,6 +36,7 @@ pub enum FieldBindType { Texture, } +// TODO: Remove this pub struct UniformInfoIter<'a, T: AsUniforms> { pub uniforms: &'a T, pub index: usize,