diff --git a/.vscode/launch.json b/.vscode/launch.json index 49a302f501..1864975ab0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "type": "lldb", "request": "launch", @@ -30,11 +31,11 @@ "cargo": { "args": [ "build", - "--example=simple_new", + "--example=scene", "--package=bevy" ], "filter": { - "name": "simple_new", + "name": "scene", "kind": "example" } }, @@ -48,9 +49,8 @@ "cargo": { "args": [ "run", - "--example=simple_new", + "--example=scene", "--package=bevy", - "--release" ], "filter": { "name": "simple_new", diff --git a/bevy_render/src/draw_target/draw_targets/assigned_meshes_draw_target.rs b/bevy_render/src/draw_target/draw_targets/assigned_meshes_draw_target.rs index 6f6cf5c604..572557a748 100644 --- a/bevy_render/src/draw_target/draw_targets/assigned_meshes_draw_target.rs +++ b/bevy_render/src/draw_target/draw_targets/assigned_meshes_draw_target.rs @@ -58,15 +58,15 @@ impl DrawTarget for AssignedMeshesDrawTarget { { let index_buffer_resource = render_resources.get_mesh_indices_resource(mesh).unwrap(); - match render_resources - .get_resource_info(index_buffer_resource) - .unwrap() - { - ResourceInfo::Buffer(buffer_info) => { - current_mesh_index_len = (buffer_info.size / 2) as u32 - } - _ => panic!("expected a buffer type"), - } + render_resources.get_resource_info( + index_buffer_resource, + &mut |resource_info| match resource_info { + Some(ResourceInfo::Buffer(buffer_info)) => { + current_mesh_index_len = (buffer_info.size / 2) as u32 + } + _ => panic!("expected a buffer type"), + }, + ); render_pass.set_index_buffer(index_buffer_resource, 0); render_pass.set_vertex_buffer(0, vertex_buffer_resource, 0); } @@ -75,7 +75,10 @@ impl DrawTarget for AssignedMeshesDrawTarget { } // TODO: validate bind group properties against shader uniform properties at least once - render_pass.set_render_resources(pipeline_descriptor, &renderable.render_resource_assignments); + render_pass.set_render_resources( + pipeline_descriptor, + &renderable.render_resource_assignments, + ); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1); } } diff --git a/bevy_render/src/draw_target/draw_targets/meshes_draw_target.rs b/bevy_render/src/draw_target/draw_targets/meshes_draw_target.rs index d27440914d..8d1d994837 100644 --- a/bevy_render/src/draw_target/draw_targets/meshes_draw_target.rs +++ b/bevy_render/src/draw_target/draw_targets/meshes_draw_target.rs @@ -37,15 +37,14 @@ impl DrawTarget for MeshesDrawTarget { { let index_buffer_resource = render_resources.get_mesh_indices_resource(*mesh).unwrap(); - match render_resources - .get_resource_info(index_buffer_resource) - .unwrap() - { - ResourceInfo::Buffer(buffer_info) => { - current_mesh_index_len = (buffer_info.size / 2) as u32 + render_resources.get_resource_info(index_buffer_resource, &mut |resource_info| { + match resource_info { + Some(ResourceInfo::Buffer(buffer_info)) => { + current_mesh_index_len = (buffer_info.size / 2) as u32 + } + _ => panic!("expected a buffer type"), } - _ => panic!("expected a buffer type"), - } + }); render_pass.set_index_buffer(index_buffer_resource, 0); render_pass.set_vertex_buffer(0, vertex_buffer_resource, 0); } @@ -54,7 +53,8 @@ impl DrawTarget for MeshesDrawTarget { } // TODO: validate bind group properties against shader uniform properties at least once - render_pass.set_render_resources(pipeline_descriptor, &renderable.render_resource_assignments); + render_pass + .set_render_resources(pipeline_descriptor, &renderable.render_resource_assignments); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1); } } diff --git a/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs b/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs index 4547ac9bf8..da37c72a72 100644 --- a/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs +++ b/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs @@ -40,18 +40,20 @@ impl DrawTarget for UiDrawTarget { }; let index_count = { + let mut index_count = None; let render_context = render_pass.get_render_context(); - if let Some(ResourceInfo::Buffer(BufferInfo { - array_info: Some(array_info), - .. - })) = render_context + render_context .resources() - .get_resource_info(ui_instances_buffer) - { - Some(array_info.item_capacity) - } else { - None - } + .get_resource_info(ui_instances_buffer, &mut |resource_info| { + if let Some(ResourceInfo::Buffer(BufferInfo { + array_info: Some(array_info), + .. + })) = resource_info + { + index_count = Some(array_info.item_capacity); + } + }); + index_count }; let global_render_resource_assignments = diff --git a/bevy_render/src/pipeline/pipeline_compiler.rs b/bevy_render/src/pipeline/pipeline_compiler.rs index 8da991f322..447ffdff2d 100644 --- a/bevy_render/src/pipeline/pipeline_compiler.rs +++ b/bevy_render/src/pipeline/pipeline_compiler.rs @@ -60,18 +60,22 @@ impl PipelineCompiler { for bind_group in layout.bind_groups.iter_mut() { for binding in bind_group.bindings.iter_mut() { if let Some(render_resource) = render_resource_assignments.get(&binding.name) { - if let Some(ResourceInfo::Buffer(BufferInfo { is_dynamic, .. })) = - render_context - .resources() - .get_resource_info(render_resource) - { - if let BindType::Uniform { - ref mut dynamic, .. - } = binding.bind_type - { - *dynamic = *is_dynamic - } - } + render_context.resources().get_resource_info( + render_resource, + &mut |resource_info| { + if let Some(ResourceInfo::Buffer(BufferInfo { + is_dynamic, .. + })) = resource_info + { + if let BindType::Uniform { + ref mut dynamic, .. + } = binding.bind_type + { + *dynamic = *is_dynamic + } + } + } + ); } } } diff --git a/bevy_render/src/render_resource/render_resource.rs b/bevy_render/src/render_resource/render_resource.rs index 56eb265d58..77124b43a5 100644 --- a/bevy_render/src/render_resource/render_resource.rs +++ b/bevy_render/src/render_resource/render_resource.rs @@ -15,7 +15,7 @@ impl RenderResource { // TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc // the overlap could cause accidents. -#[derive(Default)] +#[derive(Default, Clone)] pub struct AssetResources { texture_to_resource: HashMap, RenderResource>, texture_to_sampler_resource: HashMap, RenderResource>, diff --git a/bevy_render/src/render_resource/resource_info.rs b/bevy_render/src/render_resource/resource_info.rs index 1a86a90ea2..8b491cff6b 100644 --- a/bevy_render/src/render_resource/resource_info.rs +++ b/bevy_render/src/render_resource/resource_info.rs @@ -1,12 +1,12 @@ use crate::render_resource::BufferUsage; -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct BufferArrayInfo { pub item_size: usize, pub item_capacity: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct BufferInfo { pub size: usize, pub buffer_usage: BufferUsage, @@ -25,7 +25,7 @@ impl Default for BufferInfo { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ResourceInfo { Buffer(BufferInfo), Texture, diff --git a/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs b/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs index 3ac90bb86f..b196f76f25 100644 --- a/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs +++ b/bevy_render/src/render_resource/resource_providers/uniform_resource_provider.rs @@ -281,23 +281,27 @@ where self.uniform_buffer_status[i].as_mut().unwrap(); let (target_buffer, target_offset) = if self.use_dynamic_uniforms { let buffer = uniform_buffer_status.buffer.unwrap(); - if let Some(ResourceInfo::Buffer(BufferInfo { - array_info: Some(ref array_info), - is_dynamic: true, - .. - })) = render_resources.get_resource_info(buffer) - { - let index = uniform_buffer_status - .get_or_assign_index(render_resource_assignments.id); - render_resource_assignments.set_indexed( - &field_info.uniform_name, - buffer, - (index * array_info.item_size) as u32, - ); - (buffer, index * uniform_buffer_status.aligned_size) - } else { - panic!("Expected a dynamic uniform buffer"); - } + let mut offset = 0; + render_resources.get_resource_info(buffer, &mut |resource_info| { + if let Some(ResourceInfo::Buffer(BufferInfo { + array_info: Some(ref array_info), + is_dynamic: true, + .. + })) = resource_info + { + let index = uniform_buffer_status + .get_or_assign_index(render_resource_assignments.id); + render_resource_assignments.set_indexed( + &field_info.uniform_name, + buffer, + (index * array_info.item_size) as u32, + ); + offset = index * uniform_buffer_status.aligned_size; + } else { + panic!("Expected a dynamic uniform buffer"); + } + }); + (buffer, offset) } else { let resource = match render_resource_assignments .get(field_info.uniform_name) @@ -597,24 +601,31 @@ where align: bool, ) { let new_capacity = if let Some(buffer) = buffer_array_status.buffer { - if let Some(ResourceInfo::Buffer(BufferInfo { - array_info: Some(array_info), - .. - })) = render_context.resources().get_resource_info(buffer) - { - if array_info.item_capacity < buffer_array_status.new_item_count { - // over capacity. lets resize - Some( - buffer_array_status.new_item_count + buffer_array_status.new_item_count / 2, - ) - } else { - // under capacity. no change needed - None - } - } else { - // incorrect resource type. overwrite with new buffer - Some(buffer_array_status.new_item_count) - } + let mut new_capacity = None; + render_context + .resources() + .get_resource_info(buffer, &mut |resource_info| { + new_capacity = if let Some(ResourceInfo::Buffer(BufferInfo { + array_info: Some(array_info), + .. + })) = resource_info + { + if array_info.item_capacity < buffer_array_status.new_item_count { + // over capacity. lets resize + Some( + buffer_array_status.new_item_count + + buffer_array_status.new_item_count / 2, + ) + } else { + // under capacity. no change needed + None + } + } else { + // incorrect resource type. overwrite with new buffer + Some(buffer_array_status.new_item_count) + }; + }); + new_capacity } else { // buffer does not exist. create it now. Some(buffer_array_status.new_item_count) diff --git a/bevy_render/src/renderer_2/render_resource_context.rs b/bevy_render/src/renderer_2/render_resource_context.rs index 03b3b46375..d1733c08f6 100644 --- a/bevy_render/src/renderer_2/render_resource_context.rs +++ b/bevy_render/src/renderer_2/render_resource_context.rs @@ -9,7 +9,9 @@ use bevy_window::{Window, WindowId}; use std::any::Any; pub struct GlobalRenderResourceContext { - pub context: Box, + pub context: Box, + // TODO: why doesn't this work? + // pub context: Box, } impl GlobalRenderResourceContext { @@ -53,7 +55,7 @@ pub trait RenderResourceContext: Any { fn remove_buffer(&mut self, resource: RenderResource); fn remove_texture(&mut self, resource: RenderResource); fn remove_sampler(&mut self, resource: RenderResource); - fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo>; + fn get_resource_info(&self, resource: RenderResource, handle_info: &mut dyn FnMut(Option<&ResourceInfo>)); fn asset_resources(&self) -> &AssetResources; fn asset_resources_mut(&mut self) -> &mut AssetResources; fn get_texture_resource(&self, texture: Handle) -> Option; diff --git a/bevy_wgpu/src/renderer_2/systems.rs b/bevy_wgpu/src/renderer_2/systems.rs index d182505131..edcff8058c 100644 --- a/bevy_wgpu/src/renderer_2/systems.rs +++ b/bevy_wgpu/src/renderer_2/systems.rs @@ -30,37 +30,39 @@ pub fn render_resource_sets_system() -> Box { _| { // PERF: consider doing a par-iter over all renderable components so this can be parallelized for handle in render_graph.pipeline_descriptors.iter() { - for compiled_pipeline_handle in - pipeline_compiler.iter_compiled_pipelines(*handle).unwrap() + if let Some(compiled_pipelines) = + pipeline_compiler.iter_compiled_pipelines(*handle) { - if let Some(compiled_pipeline_assignments) = pipeline_assignments - .assignments - .get(compiled_pipeline_handle) - { - let compiled_pipeline = - pipelines.get(compiled_pipeline_handle).unwrap(); - let pipeline_layout = compiled_pipeline.get_layout().unwrap(); - - for bind_group in pipeline_layout.bind_groups.iter() { - global_render_resource_assignments - .update_render_resource_set_id(bind_group); - } - - for assignment_id in compiled_pipeline_assignments.iter() { - let entity = entity_render_resource_assignments - .get(*assignment_id) - .unwrap(); - let mut renderable = - world.get_component_mut::(*entity).unwrap(); - if !renderable.is_visible || renderable.is_instanced { - continue; - } + for compiled_pipeline_handle in compiled_pipelines { + if let Some(compiled_pipeline_assignments) = pipeline_assignments + .assignments + .get(compiled_pipeline_handle) + { + let compiled_pipeline = + pipelines.get(compiled_pipeline_handle).unwrap(); + let pipeline_layout = compiled_pipeline.get_layout().unwrap(); for bind_group in pipeline_layout.bind_groups.iter() { - renderable - .render_resource_assignments + global_render_resource_assignments .update_render_resource_set_id(bind_group); - // TODO: also setup bind groups here if possible + } + + for assignment_id in compiled_pipeline_assignments.iter() { + let entity = entity_render_resource_assignments + .get(*assignment_id) + .unwrap(); + let mut renderable = + world.get_component_mut::(*entity).unwrap(); + if !renderable.is_visible || renderable.is_instanced { + continue; + } + + for bind_group in pipeline_layout.bind_groups.iter() { + renderable + .render_resource_assignments + .update_render_resource_set_id(bind_group); + // TODO: also setup bind groups here if possible + } } } } diff --git a/bevy_wgpu/src/renderer_2/wgpu_render_context.rs b/bevy_wgpu/src/renderer_2/wgpu_render_context.rs index 7873c7a676..75c5eee5dc 100644 --- a/bevy_wgpu/src/renderer_2/wgpu_render_context.rs +++ b/bevy_wgpu/src/renderer_2/wgpu_render_context.rs @@ -1,7 +1,7 @@ use super::WgpuRenderResourceContext; use crate::{ wgpu_type_converter::{OwnedWgpuVertexBufferDescriptor, WgpuInto}, - WgpuRenderPass, + WgpuRenderPass, WgpuResourceRefs, }; use bevy_asset::{AssetStorage, Handle}; use bevy_render::{ @@ -127,10 +127,10 @@ impl RenderContext for WgpuRenderContext { if let Some((render_resource_set_id, _indices)) = render_resource_assignments.get_render_resource_set_id(bind_group_descriptor.id) { - if let None = self + if !self .render_resources .wgpu_resources - .get_bind_group(bind_group_descriptor.id, *render_resource_set_id) + .has_bind_group(bind_group_descriptor.id, *render_resource_set_id) { log::trace!( "start creating bind group for RenderResourceSet {:?}", @@ -155,48 +155,54 @@ impl RenderContext for WgpuRenderContext { .iter() .map(|binding| { if let Some(resource) = render_resource_assignments.get(&binding.name) { - let resource_info = - self.resources().get_resource_info(resource).unwrap(); - log::trace!( - "found binding {} ({}) resource: {:?} {:?}", - binding.index, - binding.name, + let mut wgpu_resource = None; + self.resources().get_resource_info( resource, - resource_info + &mut |resource_info| { + log::trace!( + "found binding {} ({}) resource: {:?} {:?}", + binding.index, + binding.name, + resource, + resource_info + ); + wgpu_resource = match &binding.bind_type { + BindType::SampledTexture { .. } => { + if let Some(ResourceInfo::Texture) = resource_info { + let texture = textures.get(&resource).unwrap(); + Some(wgpu::BindingResource::TextureView(texture)) + } else { + panic!("expected a Texture resource"); + } + } + BindType::Sampler { .. } => { + if let Some(ResourceInfo::Sampler) = resource_info { + let sampler = samplers.get(&resource).unwrap(); + Some(wgpu::BindingResource::Sampler(sampler)) + } else { + panic!("expected a Sampler resource"); + } + } + BindType::Uniform { .. } => { + if let Some(ResourceInfo::Buffer(buffer_info)) = + resource_info + { + let buffer = buffers.get(&resource).unwrap(); + Some(wgpu::BindingResource::Buffer { + buffer, + range: 0..buffer_info.size as u64, + }) + } else { + panic!("expected a Buffer resource"); + } + } + _ => panic!("unsupported bind type"), + } + }, ); wgpu::Binding { binding: binding.index, - resource: match &binding.bind_type { - BindType::SampledTexture { .. } => { - if let ResourceInfo::Texture = resource_info { - let texture = textures.get(&resource).unwrap(); - wgpu::BindingResource::TextureView(texture) - } else { - panic!("expected a Texture resource"); - } - } - BindType::Sampler { .. } => { - if let ResourceInfo::Sampler = resource_info { - let sampler = samplers.get(&resource).unwrap(); - wgpu::BindingResource::Sampler(sampler) - } else { - panic!("expected a Sampler resource"); - } - } - BindType::Uniform { .. } => { - if let ResourceInfo::Buffer(buffer_info) = resource_info - { - let buffer = buffers.get(&resource).unwrap(); - wgpu::BindingResource::Buffer { - buffer, - range: 0..buffer_info.size as u64, - } - } else { - panic!("expected a Buffer resource"); - } - } - _ => panic!("unsupported bind type"), - }, + resource: wgpu_resource.expect("No resource binding found"), } } else { panic!( @@ -256,13 +262,13 @@ impl RenderContext for WgpuRenderContext { let layout = pipeline_descriptor.get_layout().unwrap(); for bind_group in layout.bind_groups.iter() { - if let None = self + if self .render_resources .wgpu_resources .bind_group_layouts .read() .unwrap() - .get(&bind_group.id) + .get(&bind_group.id).is_none() { let bind_group_layout_binding = bind_group .bindings @@ -412,18 +418,21 @@ impl RenderContext for WgpuRenderContext { if !self.command_encoder.is_some() { self.command_encoder.create(&self.device); } - + let resource_lock = self.render_resources.wgpu_resources.read(); + let refs = resource_lock.refs(); let mut encoder = self.command_encoder.take().unwrap(); { let render_pass = create_render_pass( self, pass_descriptor, render_resource_assignments, + &refs, &mut encoder, ); let mut wgpu_render_pass = WgpuRenderPass { render_context: self, render_pass, + render_resources: refs, bound_bind_groups: HashMap::default(), }; @@ -438,6 +447,7 @@ pub fn create_render_pass<'a, 'b>( render_context: &'a WgpuRenderContext, pass_descriptor: &PassDescriptor, global_render_resource_assignments: &'b RenderResourceAssignments, + refs: &WgpuResourceRefs<'a>, encoder: &'a mut wgpu::CommandEncoder, ) -> wgpu::RenderPass<'a> { encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -448,6 +458,7 @@ pub fn create_render_pass<'a, 'b>( create_wgpu_color_attachment_descriptor( render_context, global_render_resource_assignments, + refs, c, ) }) @@ -456,6 +467,7 @@ pub fn create_render_pass<'a, 'b>( create_wgpu_depth_stencil_attachment_descriptor( render_context, global_render_resource_assignments, + refs, d, ) }), @@ -465,16 +477,13 @@ pub fn create_render_pass<'a, 'b>( fn get_texture_view<'a>( render_context: &'a WgpuRenderContext, global_render_resource_assignments: &RenderResourceAssignments, + refs: &WgpuResourceRefs<'a>, name: &str, ) -> &'a wgpu::TextureView { match name { resource_name::texture::SWAP_CHAIN => { - if let Some(primary_swap_chain) = render_context - .render_resources - .wgpu_resources + if let Some(primary_swap_chain) = refs .swap_chain_outputs - .read() - .unwrap() .get(render_context.primary_window.as_ref().unwrap()) .map(|output| &output.view) { @@ -484,14 +493,7 @@ fn get_texture_view<'a>( } } _ => match global_render_resource_assignments.get(name) { - Some(resource) => render_context - .render_resources - .wgpu_resources - .textures - .read() - .unwrap() - .get(&resource) - .unwrap(), + Some(resource) => refs.textures.get(&resource).unwrap(), None => { // if let Some(swap_chain_output) = swap_chain_outputs.get(name) { // &swap_chain_output.view @@ -506,11 +508,13 @@ fn get_texture_view<'a>( fn create_wgpu_color_attachment_descriptor<'a>( render_context: &'a WgpuRenderContext, global_render_resource_assignments: &RenderResourceAssignments, + refs: &WgpuResourceRefs<'a>, color_attachment_descriptor: &RenderPassColorAttachmentDescriptor, ) -> wgpu::RenderPassColorAttachmentDescriptor<'a> { let attachment = get_texture_view( render_context, global_render_resource_assignments, + refs, color_attachment_descriptor.attachment.as_str(), ); @@ -521,6 +525,7 @@ fn create_wgpu_color_attachment_descriptor<'a>( get_texture_view( render_context, global_render_resource_assignments, + refs, target.as_str(), ) }); @@ -537,11 +542,13 @@ fn create_wgpu_color_attachment_descriptor<'a>( fn create_wgpu_depth_stencil_attachment_descriptor<'a>( render_context: &'a WgpuRenderContext, global_render_resource_assignments: &RenderResourceAssignments, + refs: &WgpuResourceRefs<'a>, depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor, ) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<'a> { let attachment = get_texture_view( render_context, global_render_resource_assignments, + refs, depth_stencil_attachment_descriptor.attachment.as_str(), ); diff --git a/bevy_wgpu/src/renderer_2/wgpu_render_resource_context.rs b/bevy_wgpu/src/renderer_2/wgpu_render_resource_context.rs index 68f6b73512..94be1ffe91 100644 --- a/bevy_wgpu/src/renderer_2/wgpu_render_resource_context.rs +++ b/bevy_wgpu/src/renderer_2/wgpu_render_resource_context.rs @@ -16,7 +16,7 @@ use bevy_window::{WindowId, Window}; #[derive(Clone)] pub struct WgpuRenderResourceContext { pub device: Arc, - pub wgpu_resources: Arc, + pub wgpu_resources: WgpuResources, } @@ -24,7 +24,7 @@ impl WgpuRenderResourceContext { pub fn new(device: Arc) -> Self { WgpuRenderResourceContext { device, - wgpu_resources: Arc::new(WgpuResources::default()), + wgpu_resources: WgpuResources::default(), } } } @@ -95,8 +95,8 @@ impl RenderResourceContext for WgpuRenderResourceContext { .get_mesh_indices_resource(mesh) } - fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> { - self.wgpu_resources.get_resource_info(resource) + fn get_resource_info(&self, resource: RenderResource, handle_info: &mut dyn FnMut(Option<&ResourceInfo>)) { + self.wgpu_resources.get_resource_info(resource, handle_info); } fn asset_resources(&self) -> &AssetResources { diff --git a/bevy_wgpu/src/wgpu_render_pass.rs b/bevy_wgpu/src/wgpu_render_pass.rs index fb8d4fbed8..a26aced02e 100644 --- a/bevy_wgpu/src/wgpu_render_pass.rs +++ b/bevy_wgpu/src/wgpu_render_pass.rs @@ -1,4 +1,4 @@ -use crate::renderer_2::WgpuRenderContext; +use crate::{renderer_2::WgpuRenderContext, WgpuResourceRefs}; use bevy_asset::Handle; use bevy_render::{ pass::RenderPass, @@ -13,6 +13,7 @@ use std::{collections::HashMap, ops::Range}; pub struct WgpuRenderPass<'a> { pub render_pass: wgpu::RenderPass<'a>, pub render_context: &'a WgpuRenderContext, + pub render_resources: WgpuResourceRefs<'a>, pub bound_bind_groups: HashMap, } @@ -22,27 +23,21 @@ impl<'a> RenderPass for WgpuRenderPass<'a> { } fn set_vertex_buffer(&mut self, start_slot: u32, resource: RenderResource, offset: u64) { - let buffers = self - .render_context + let buffer = self .render_resources - .wgpu_resources .buffers - .read() + .get(&resource) .unwrap(); - let buffer = buffers.get(&resource).unwrap(); self.render_pass .set_vertex_buffer(start_slot, &buffer, offset, 0); } fn set_index_buffer(&mut self, resource: RenderResource, offset: u64) { - let buffers = self - .render_context + let buffer = self .render_resources - .wgpu_resources .buffers - .read() + .get(&resource) .unwrap(); - let buffer = buffers.get(&resource).unwrap(); self.render_pass.set_index_buffer(&buffer, offset, 0); } @@ -80,17 +75,15 @@ impl<'a> RenderPass for WgpuRenderPass<'a> { index_buffer ); self.set_index_buffer(index_buffer, 0); - match self - .render_context - .resources() - .get_resource_info(index_buffer) - .unwrap() - { - ResourceInfo::Buffer(buffer_info) => { - indices = Some(0..(buffer_info.size / 2) as u32) - } - _ => panic!("expected a buffer type"), - } + self.render_context.resources().get_resource_info( + index_buffer, + &mut |resource_info| match resource_info { + Some(ResourceInfo::Buffer(buffer_info)) => { + indices = Some(0..(buffer_info.size / 2) as u32) + } + _ => panic!("expected a buffer type"), + }, + ); } } } @@ -99,50 +92,53 @@ impl<'a> RenderPass for WgpuRenderPass<'a> { if let Some((render_resource_set_id, dynamic_uniform_indices)) = render_resource_assignments.get_render_resource_set_id(bind_group.id) { - if let Some(wgpu_bind_group) = self - .render_context + if let Some(bind_group_info) = self .render_resources - .wgpu_resources - .get_bind_group(bind_group.id, *render_resource_set_id) + .bind_groups + .get(&bind_group.id) { - const EMPTY: &'static [u32] = &[]; - let dynamic_uniform_indices = - if let Some(dynamic_uniform_indices) = dynamic_uniform_indices { - dynamic_uniform_indices.as_slice() - } else { - EMPTY - }; - - // don't bind bind groups if they are already set - // TODO: these checks come at a performance cost. make sure its worth it! - if let Some(bound_render_resource_set) = - self.bound_bind_groups.get(&bind_group.index) + if let Some(wgpu_bind_group) = + bind_group_info.bind_groups.get(render_resource_set_id) { - if *bound_render_resource_set == *render_resource_set_id - && dynamic_uniform_indices.len() == 0 + const EMPTY: &'static [u32] = &[]; + let dynamic_uniform_indices = + if let Some(dynamic_uniform_indices) = dynamic_uniform_indices { + dynamic_uniform_indices.as_slice() + } else { + EMPTY + }; + + // don't bind bind groups if they are already set + // TODO: these checks come at a performance cost. make sure its worth it! + if let Some(bound_render_resource_set) = + self.bound_bind_groups.get(&bind_group.index) { - continue; + if *bound_render_resource_set == *render_resource_set_id + && dynamic_uniform_indices.len() == 0 + { + continue; + } } - } - if dynamic_uniform_indices.len() == 0 { - self.bound_bind_groups - .insert(bind_group.index, *render_resource_set_id); - } else { - self.bound_bind_groups.remove(&bind_group.index); - } + if dynamic_uniform_indices.len() == 0 { + self.bound_bind_groups + .insert(bind_group.index, *render_resource_set_id); + } else { + self.bound_bind_groups.remove(&bind_group.index); + } - log::trace!( - "set bind group {} {:?}: {:?}", - bind_group.index, - dynamic_uniform_indices, - render_resource_set_id - ); - self.render_pass.set_bind_group( - bind_group.index, - &wgpu_bind_group, - dynamic_uniform_indices, - ); + log::trace!( + "set bind group {} {:?}: {:?}", + bind_group.index, + dynamic_uniform_indices, + render_resource_set_id + ); + self.render_pass.set_bind_group( + bind_group.index, + wgpu_bind_group, + dynamic_uniform_indices, + ); + } }; } } @@ -150,14 +146,7 @@ impl<'a> RenderPass for WgpuRenderPass<'a> { indices } fn set_pipeline(&mut self, pipeline_handle: Handle) { - let render_pipelines = self - .render_context - .render_resources - .wgpu_resources - .render_pipelines - .read() - .unwrap(); - let pipeline = render_pipelines.get(&pipeline_handle).expect( + let pipeline = self.render_resources.render_pipelines.get(&pipeline_handle).expect( "Attempted to use a pipeline that does not exist in this RenderPass's RenderContext", ); self.render_pass.set_pipeline(pipeline); diff --git a/bevy_wgpu/src/wgpu_renderer.rs b/bevy_wgpu/src/wgpu_renderer.rs index 2a53c8d7be..da303a30b3 100644 --- a/bevy_wgpu/src/wgpu_renderer.rs +++ b/bevy_wgpu/src/wgpu_renderer.rs @@ -11,7 +11,7 @@ use bevy_render::{ }; use bevy_window::{WindowCreated, WindowResized, Windows}; use legion::prelude::*; -use std::{any::Any, collections::HashSet, ops::Deref, sync::Arc}; +use std::{collections::HashSet, ops::Deref, sync::Arc}; pub struct WgpuRenderer { pub device: Arc, @@ -131,7 +131,6 @@ impl WgpuRenderer { for resource_provider_chunk in render_graph.resource_providers.chunks_mut(chunk_size) { // TODO: try to unify this Device usage let device = self.device.clone(); - let resource_device = device.clone(); // let sender = sender.clone(); // s.spawn(|_| { // TODO: replace WgpuResources with Global+Local resources @@ -241,17 +240,11 @@ impl WgpuRenderer { let mut encoder = { let mut global_context = resources.get_mut::().unwrap(); - let mut any_context = (&mut global_context.context) as &mut dyn Any; - // let mut any_context = (&mut global_context.context); - // println!("{}", std::any::type_name_of_val(any_context)); - let mut render_resource_context = any_context + let render_resource_context = global_context + .context .downcast_mut::() .unwrap(); - panic!("ahh"); - // let mut render_resource_context = any_context - // .downcast_mut::() - // .unwrap(); self.handle_window_created_events(resources, render_resource_context); self.handle_window_resized_events(resources, render_resource_context); @@ -266,11 +259,7 @@ impl WgpuRenderer { self.intialized = true; } - self.update_resource_providers( - world, - resources, - render_resource_context, - ); + self.update_resource_providers(world, resources, render_resource_context); update_shader_assignments(world, resources, &render_context); self.create_queued_textures(resources, &mut render_context.render_resources); @@ -280,10 +269,8 @@ impl WgpuRenderer { // TODO: add to POST_UPDATE and remove redundant global_context render_resource_sets_system().run(world, resources); let mut global_context = resources.get_mut::().unwrap(); - let mut any_context = (&mut global_context.context) as &mut dyn Any; - // let mut any_context = (&mut global_context.context); - // println!("{}", std::any::type_name_of_val(any_context)); - let mut render_resource_context = any_context + let render_resource_context = global_context + .context .downcast_mut::() .unwrap(); let mut render_context = diff --git a/bevy_wgpu/src/wgpu_resources.rs b/bevy_wgpu/src/wgpu_resources.rs index 0baa974cdc..8887e8ef0e 100644 --- a/bevy_wgpu/src/wgpu_resources.rs +++ b/bevy_wgpu/src/wgpu_resources.rs @@ -20,12 +20,56 @@ pub struct WgpuBindGroupInfo { pub bind_groups: HashMap, } +/// Grabs a read lock on all wgpu resources. When paired with WgpuResourceRefs, this allows +/// us to pass in wgpu resources to wgpu::RenderPass<'a> with the appropriate lifetime. This is accomplished by +/// grabbing a WgpuResourcesReadLock _before_ creating a wgpu::RenderPass, getting a WgpuResourcesRefs, and storing that +/// in the pass. +/// +/// This is only a problem because RwLockReadGuard.read() erases the guard's lifetime and creates a new anonymous lifetime. If +/// you call RwLockReadGuard.read() during a pass, the reference will have an anonymous lifetime that lives for less than the +/// pass. +/// +/// The biggest implication of this design (other than the additional boilerplate here) is that beginning a render pass +/// blocks writes to these resources. This means that if the pass attempts to write any resource, a deadlock will occur. It also means +/// that other threads attempting to write resources will need to wait for pass encoding to finish. Almost all writes should occur before +/// passes start, so this hopefully won't be a problem. +/// +/// It is worth comparing the performance of this to transactional / copy-based approach. This design lock based design guarantees +/// consistency, doesn't perform redundant allocations, and only blocks when a write is occurring. A copy based approach would +/// never block, but would require more allocations / state-synchronization, which I expect will be more expensive. +/// +/// Single threaded implementations don't need to worry about these lifetimes constraints at all. RenderPasses can use a RenderContext's +/// WgpuResources directly. RenderContext already has a lifetime greater than the RenderPass. pub struct WgpuResourcesReadLock<'a> { pub buffers: RwLockReadGuard<'a, HashMap>, pub textures: RwLockReadGuard<'a, HashMap>, + pub swap_chain_outputs: RwLockReadGuard<'a, HashMap>, + pub render_pipelines: RwLockReadGuard<'a, HashMap, wgpu::RenderPipeline>>, + pub bind_groups: RwLockReadGuard<'a, HashMap>, } -#[derive(Default)] +impl<'a> WgpuResourcesReadLock<'a> { + pub fn refs(&'a self) -> WgpuResourceRefs<'a> { + WgpuResourceRefs { + buffers: &self.buffers, + textures: &self.textures, + swap_chain_outputs: &self.swap_chain_outputs, + render_pipelines: &self.render_pipelines, + bind_groups: &self.bind_groups, + } + } +} + +/// Stores read only references to WgpuResource collections. See WgpuResourcesReadLock docs for context on why this exists +pub struct WgpuResourceRefs<'a> { + pub buffers: &'a HashMap, + pub textures: &'a HashMap, + pub swap_chain_outputs: &'a HashMap, + pub render_pipelines: &'a HashMap, wgpu::RenderPipeline>, + pub bind_groups: &'a HashMap, +} + +#[derive(Default, Clone)] pub struct WgpuResources { // TODO: remove this from WgpuResources. it doesn't need to be here pub asset_resources: AssetResources, @@ -43,10 +87,13 @@ pub struct WgpuResources { } impl WgpuResources { - pub fn read(&self) -> WgpuResourcesReadLock { + pub fn read<'a>(&'a self) -> WgpuResourcesReadLock<'a> { WgpuResourcesReadLock { buffers: self.buffers.read().unwrap(), textures: self.textures.read().unwrap(), + swap_chain_outputs: self.swap_chain_outputs.read().unwrap(), + render_pipelines: self.render_pipelines.read().unwrap(), + bind_groups: self.bind_groups.read().unwrap(), } } @@ -100,20 +147,20 @@ impl WgpuResources { .insert(resource, resource_info); } - pub fn get_bind_group( + pub fn has_bind_group( &self, bind_group_descriptor_id: BindGroupDescriptorId, render_resource_set_id: RenderResourceSetId, - ) -> Option<&wgpu::BindGroup> { + ) -> bool { if let Some(bind_group_info) = self .bind_groups .read() .unwrap() .get(&bind_group_descriptor_id) { - bind_group_info.bind_groups.get(&render_resource_set_id) + bind_group_info.bind_groups.get(&render_resource_set_id).is_some() } else { - None + false } } @@ -174,8 +221,11 @@ impl WgpuResources { self.assign_buffer(buffer, buffer_info) } - pub fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> { - self.resource_info.read().unwrap().get(&resource) + // TODO: taking a closure isn't fantastic. is there any way to make this better without exposing the lock in the interface? + pub fn get_resource_info(&self, resource: RenderResource, handle_info: &mut dyn FnMut(Option<&ResourceInfo>)) { + let resource_info = self.resource_info.read().unwrap(); + let info = resource_info.get(&resource); + handle_info(info); } pub fn remove_buffer(&self, resource: RenderResource) {