RwLock WgpuResources works now!

This commit is contained in:
Carter Anderson 2020-04-15 11:42:56 -07:00
parent b53f198b99
commit 318b7dee1a
15 changed files with 325 additions and 268 deletions

8
.vscode/launch.json vendored
View File

@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
@ -30,11 +31,11 @@
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--example=simple_new", "--example=scene",
"--package=bevy" "--package=bevy"
], ],
"filter": { "filter": {
"name": "simple_new", "name": "scene",
"kind": "example" "kind": "example"
} }
}, },
@ -48,9 +49,8 @@
"cargo": { "cargo": {
"args": [ "args": [
"run", "run",
"--example=simple_new", "--example=scene",
"--package=bevy", "--package=bevy",
"--release"
], ],
"filter": { "filter": {
"name": "simple_new", "name": "simple_new",

View File

@ -58,15 +58,15 @@ impl DrawTarget for AssignedMeshesDrawTarget {
{ {
let index_buffer_resource = let index_buffer_resource =
render_resources.get_mesh_indices_resource(mesh).unwrap(); render_resources.get_mesh_indices_resource(mesh).unwrap();
match render_resources render_resources.get_resource_info(
.get_resource_info(index_buffer_resource) index_buffer_resource,
.unwrap() &mut |resource_info| match resource_info {
{ Some(ResourceInfo::Buffer(buffer_info)) => {
ResourceInfo::Buffer(buffer_info) => { current_mesh_index_len = (buffer_info.size / 2) as u32
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_index_buffer(index_buffer_resource, 0);
render_pass.set_vertex_buffer(0, vertex_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 // 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); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
} }
} }

View File

@ -37,15 +37,14 @@ impl DrawTarget for MeshesDrawTarget {
{ {
let index_buffer_resource = let index_buffer_resource =
render_resources.get_mesh_indices_resource(*mesh).unwrap(); render_resources.get_mesh_indices_resource(*mesh).unwrap();
match render_resources render_resources.get_resource_info(index_buffer_resource, &mut |resource_info| {
.get_resource_info(index_buffer_resource) match resource_info {
.unwrap() Some(ResourceInfo::Buffer(buffer_info)) => {
{ current_mesh_index_len = (buffer_info.size / 2) as u32
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_index_buffer(index_buffer_resource, 0);
render_pass.set_vertex_buffer(0, vertex_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 // 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); render_pass.draw_indexed(0..current_mesh_index_len, 0, 0..1);
} }
} }

View File

@ -40,18 +40,20 @@ impl DrawTarget for UiDrawTarget {
}; };
let index_count = { let index_count = {
let mut index_count = None;
let render_context = render_pass.get_render_context(); let render_context = render_pass.get_render_context();
if let Some(ResourceInfo::Buffer(BufferInfo { render_context
array_info: Some(array_info),
..
})) = render_context
.resources() .resources()
.get_resource_info(ui_instances_buffer) .get_resource_info(ui_instances_buffer, &mut |resource_info| {
{ if let Some(ResourceInfo::Buffer(BufferInfo {
Some(array_info.item_capacity) array_info: Some(array_info),
} else { ..
None })) = resource_info
} {
index_count = Some(array_info.item_capacity);
}
});
index_count
}; };
let global_render_resource_assignments = let global_render_resource_assignments =

View File

@ -60,18 +60,22 @@ impl PipelineCompiler {
for bind_group in layout.bind_groups.iter_mut() { for bind_group in layout.bind_groups.iter_mut() {
for binding in bind_group.bindings.iter_mut() { for binding in bind_group.bindings.iter_mut() {
if let Some(render_resource) = render_resource_assignments.get(&binding.name) { 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_context render_resource,
.resources() &mut |resource_info| {
.get_resource_info(render_resource) if let Some(ResourceInfo::Buffer(BufferInfo {
{ is_dynamic, ..
if let BindType::Uniform { })) = resource_info
ref mut dynamic, .. {
} = binding.bind_type if let BindType::Uniform {
{ ref mut dynamic, ..
*dynamic = *is_dynamic } = binding.bind_type
} {
} *dynamic = *is_dynamic
}
}
}
);
} }
} }
} }

View File

@ -15,7 +15,7 @@ impl RenderResource {
// TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc // TODO: consider scoping breaking these mappings up by type: Texture, Sampler, etc
// the overlap could cause accidents. // the overlap could cause accidents.
#[derive(Default)] #[derive(Default, Clone)]
pub struct AssetResources { pub struct AssetResources {
texture_to_resource: HashMap<Handle<Texture>, RenderResource>, texture_to_resource: HashMap<Handle<Texture>, RenderResource>,
texture_to_sampler_resource: HashMap<Handle<Texture>, RenderResource>, texture_to_sampler_resource: HashMap<Handle<Texture>, RenderResource>,

View File

@ -1,12 +1,12 @@
use crate::render_resource::BufferUsage; use crate::render_resource::BufferUsage;
#[derive(Default, Debug)] #[derive(Default, Debug, Clone)]
pub struct BufferArrayInfo { pub struct BufferArrayInfo {
pub item_size: usize, pub item_size: usize,
pub item_capacity: usize, pub item_capacity: usize,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct BufferInfo { pub struct BufferInfo {
pub size: usize, pub size: usize,
pub buffer_usage: BufferUsage, pub buffer_usage: BufferUsage,
@ -25,7 +25,7 @@ impl Default for BufferInfo {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum ResourceInfo { pub enum ResourceInfo {
Buffer(BufferInfo), Buffer(BufferInfo),
Texture, Texture,

View File

@ -281,23 +281,27 @@ where
self.uniform_buffer_status[i].as_mut().unwrap(); self.uniform_buffer_status[i].as_mut().unwrap();
let (target_buffer, target_offset) = if self.use_dynamic_uniforms { let (target_buffer, target_offset) = if self.use_dynamic_uniforms {
let buffer = uniform_buffer_status.buffer.unwrap(); let buffer = uniform_buffer_status.buffer.unwrap();
if let Some(ResourceInfo::Buffer(BufferInfo { let mut offset = 0;
array_info: Some(ref array_info), render_resources.get_resource_info(buffer, &mut |resource_info| {
is_dynamic: true, if let Some(ResourceInfo::Buffer(BufferInfo {
.. array_info: Some(ref array_info),
})) = render_resources.get_resource_info(buffer) is_dynamic: true,
{ ..
let index = uniform_buffer_status })) = resource_info
.get_or_assign_index(render_resource_assignments.id); {
render_resource_assignments.set_indexed( let index = uniform_buffer_status
&field_info.uniform_name, .get_or_assign_index(render_resource_assignments.id);
buffer, render_resource_assignments.set_indexed(
(index * array_info.item_size) as u32, &field_info.uniform_name,
); buffer,
(buffer, index * uniform_buffer_status.aligned_size) (index * array_info.item_size) as u32,
} else { );
panic!("Expected a dynamic uniform buffer"); offset = index * uniform_buffer_status.aligned_size;
} } else {
panic!("Expected a dynamic uniform buffer");
}
});
(buffer, offset)
} else { } else {
let resource = match render_resource_assignments let resource = match render_resource_assignments
.get(field_info.uniform_name) .get(field_info.uniform_name)
@ -597,24 +601,31 @@ where
align: bool, align: bool,
) { ) {
let new_capacity = if let Some(buffer) = buffer_array_status.buffer { let new_capacity = if let Some(buffer) = buffer_array_status.buffer {
if let Some(ResourceInfo::Buffer(BufferInfo { let mut new_capacity = None;
array_info: Some(array_info), render_context
.. .resources()
})) = render_context.resources().get_resource_info(buffer) .get_resource_info(buffer, &mut |resource_info| {
{ new_capacity = if let Some(ResourceInfo::Buffer(BufferInfo {
if array_info.item_capacity < buffer_array_status.new_item_count { array_info: Some(array_info),
// over capacity. lets resize ..
Some( })) = resource_info
buffer_array_status.new_item_count + buffer_array_status.new_item_count / 2, {
) if array_info.item_capacity < buffer_array_status.new_item_count {
} else { // over capacity. lets resize
// under capacity. no change needed Some(
None buffer_array_status.new_item_count
} + buffer_array_status.new_item_count / 2,
} else { )
// incorrect resource type. overwrite with new buffer } else {
Some(buffer_array_status.new_item_count) // under capacity. no change needed
} None
}
} else {
// incorrect resource type. overwrite with new buffer
Some(buffer_array_status.new_item_count)
};
});
new_capacity
} else { } else {
// buffer does not exist. create it now. // buffer does not exist. create it now.
Some(buffer_array_status.new_item_count) Some(buffer_array_status.new_item_count)

View File

@ -9,7 +9,9 @@ use bevy_window::{Window, WindowId};
use std::any::Any; use std::any::Any;
pub struct GlobalRenderResourceContext { pub struct GlobalRenderResourceContext {
pub context: Box<dyn RenderResourceContext + Send + Sync + 'static>, pub context: Box<dyn Any + Send + Sync + 'static>,
// TODO: why doesn't this work?
// pub context: Box<dyn RenderResourceContext + Send + Sync + 'static>,
} }
impl GlobalRenderResourceContext { impl GlobalRenderResourceContext {
@ -53,7 +55,7 @@ pub trait RenderResourceContext: Any {
fn remove_buffer(&mut self, resource: RenderResource); fn remove_buffer(&mut self, resource: RenderResource);
fn remove_texture(&mut self, resource: RenderResource); fn remove_texture(&mut self, resource: RenderResource);
fn remove_sampler(&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(&self) -> &AssetResources;
fn asset_resources_mut(&mut self) -> &mut AssetResources; fn asset_resources_mut(&mut self) -> &mut AssetResources;
fn get_texture_resource(&self, texture: Handle<Texture>) -> Option<RenderResource>; fn get_texture_resource(&self, texture: Handle<Texture>) -> Option<RenderResource>;

View File

@ -30,37 +30,39 @@ pub fn render_resource_sets_system() -> Box<dyn Schedulable> {
_| { _| {
// PERF: consider doing a par-iter over all renderable components so this can be parallelized // PERF: consider doing a par-iter over all renderable components so this can be parallelized
for handle in render_graph.pipeline_descriptors.iter() { for handle in render_graph.pipeline_descriptors.iter() {
for compiled_pipeline_handle in if let Some(compiled_pipelines) =
pipeline_compiler.iter_compiled_pipelines(*handle).unwrap() pipeline_compiler.iter_compiled_pipelines(*handle)
{ {
if let Some(compiled_pipeline_assignments) = pipeline_assignments for compiled_pipeline_handle in compiled_pipelines {
.assignments if let Some(compiled_pipeline_assignments) = pipeline_assignments
.get(compiled_pipeline_handle) .assignments
{ .get(compiled_pipeline_handle)
let compiled_pipeline = {
pipelines.get(compiled_pipeline_handle).unwrap(); let compiled_pipeline =
let pipeline_layout = compiled_pipeline.get_layout().unwrap(); 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::<Renderable>(*entity).unwrap();
if !renderable.is_visible || renderable.is_instanced {
continue;
}
for bind_group in pipeline_layout.bind_groups.iter() { for bind_group in pipeline_layout.bind_groups.iter() {
renderable global_render_resource_assignments
.render_resource_assignments
.update_render_resource_set_id(bind_group); .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::<Renderable>(*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
}
} }
} }
} }

View File

@ -1,7 +1,7 @@
use super::WgpuRenderResourceContext; use super::WgpuRenderResourceContext;
use crate::{ use crate::{
wgpu_type_converter::{OwnedWgpuVertexBufferDescriptor, WgpuInto}, wgpu_type_converter::{OwnedWgpuVertexBufferDescriptor, WgpuInto},
WgpuRenderPass, WgpuRenderPass, WgpuResourceRefs,
}; };
use bevy_asset::{AssetStorage, Handle}; use bevy_asset::{AssetStorage, Handle};
use bevy_render::{ use bevy_render::{
@ -127,10 +127,10 @@ impl RenderContext for WgpuRenderContext {
if let Some((render_resource_set_id, _indices)) = if let Some((render_resource_set_id, _indices)) =
render_resource_assignments.get_render_resource_set_id(bind_group_descriptor.id) render_resource_assignments.get_render_resource_set_id(bind_group_descriptor.id)
{ {
if let None = self if !self
.render_resources .render_resources
.wgpu_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!( log::trace!(
"start creating bind group for RenderResourceSet {:?}", "start creating bind group for RenderResourceSet {:?}",
@ -155,48 +155,54 @@ impl RenderContext for WgpuRenderContext {
.iter() .iter()
.map(|binding| { .map(|binding| {
if let Some(resource) = render_resource_assignments.get(&binding.name) { if let Some(resource) = render_resource_assignments.get(&binding.name) {
let resource_info = let mut wgpu_resource = None;
self.resources().get_resource_info(resource).unwrap(); self.resources().get_resource_info(
log::trace!(
"found binding {} ({}) resource: {:?} {:?}",
binding.index,
binding.name,
resource, 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 { wgpu::Binding {
binding: binding.index, binding: binding.index,
resource: match &binding.bind_type { resource: wgpu_resource.expect("No resource binding found"),
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"),
},
} }
} else { } else {
panic!( panic!(
@ -256,13 +262,13 @@ impl RenderContext for WgpuRenderContext {
let layout = pipeline_descriptor.get_layout().unwrap(); let layout = pipeline_descriptor.get_layout().unwrap();
for bind_group in layout.bind_groups.iter() { for bind_group in layout.bind_groups.iter() {
if let None = self if self
.render_resources .render_resources
.wgpu_resources .wgpu_resources
.bind_group_layouts .bind_group_layouts
.read() .read()
.unwrap() .unwrap()
.get(&bind_group.id) .get(&bind_group.id).is_none()
{ {
let bind_group_layout_binding = bind_group let bind_group_layout_binding = bind_group
.bindings .bindings
@ -412,18 +418,21 @@ impl RenderContext for WgpuRenderContext {
if !self.command_encoder.is_some() { if !self.command_encoder.is_some() {
self.command_encoder.create(&self.device); 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 mut encoder = self.command_encoder.take().unwrap();
{ {
let render_pass = create_render_pass( let render_pass = create_render_pass(
self, self,
pass_descriptor, pass_descriptor,
render_resource_assignments, render_resource_assignments,
&refs,
&mut encoder, &mut encoder,
); );
let mut wgpu_render_pass = WgpuRenderPass { let mut wgpu_render_pass = WgpuRenderPass {
render_context: self, render_context: self,
render_pass, render_pass,
render_resources: refs,
bound_bind_groups: HashMap::default(), bound_bind_groups: HashMap::default(),
}; };
@ -438,6 +447,7 @@ pub fn create_render_pass<'a, 'b>(
render_context: &'a WgpuRenderContext, render_context: &'a WgpuRenderContext,
pass_descriptor: &PassDescriptor, pass_descriptor: &PassDescriptor,
global_render_resource_assignments: &'b RenderResourceAssignments, global_render_resource_assignments: &'b RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
encoder: &'a mut wgpu::CommandEncoder, encoder: &'a mut wgpu::CommandEncoder,
) -> wgpu::RenderPass<'a> { ) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -448,6 +458,7 @@ pub fn create_render_pass<'a, 'b>(
create_wgpu_color_attachment_descriptor( create_wgpu_color_attachment_descriptor(
render_context, render_context,
global_render_resource_assignments, global_render_resource_assignments,
refs,
c, c,
) )
}) })
@ -456,6 +467,7 @@ pub fn create_render_pass<'a, 'b>(
create_wgpu_depth_stencil_attachment_descriptor( create_wgpu_depth_stencil_attachment_descriptor(
render_context, render_context,
global_render_resource_assignments, global_render_resource_assignments,
refs,
d, d,
) )
}), }),
@ -465,16 +477,13 @@ pub fn create_render_pass<'a, 'b>(
fn get_texture_view<'a>( fn get_texture_view<'a>(
render_context: &'a WgpuRenderContext, render_context: &'a WgpuRenderContext,
global_render_resource_assignments: &RenderResourceAssignments, global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
name: &str, name: &str,
) -> &'a wgpu::TextureView { ) -> &'a wgpu::TextureView {
match name { match name {
resource_name::texture::SWAP_CHAIN => { resource_name::texture::SWAP_CHAIN => {
if let Some(primary_swap_chain) = render_context if let Some(primary_swap_chain) = refs
.render_resources
.wgpu_resources
.swap_chain_outputs .swap_chain_outputs
.read()
.unwrap()
.get(render_context.primary_window.as_ref().unwrap()) .get(render_context.primary_window.as_ref().unwrap())
.map(|output| &output.view) .map(|output| &output.view)
{ {
@ -484,14 +493,7 @@ fn get_texture_view<'a>(
} }
} }
_ => match global_render_resource_assignments.get(name) { _ => match global_render_resource_assignments.get(name) {
Some(resource) => render_context Some(resource) => refs.textures.get(&resource).unwrap(),
.render_resources
.wgpu_resources
.textures
.read()
.unwrap()
.get(&resource)
.unwrap(),
None => { None => {
// if let Some(swap_chain_output) = swap_chain_outputs.get(name) { // if let Some(swap_chain_output) = swap_chain_outputs.get(name) {
// &swap_chain_output.view // &swap_chain_output.view
@ -506,11 +508,13 @@ fn get_texture_view<'a>(
fn create_wgpu_color_attachment_descriptor<'a>( fn create_wgpu_color_attachment_descriptor<'a>(
render_context: &'a WgpuRenderContext, render_context: &'a WgpuRenderContext,
global_render_resource_assignments: &RenderResourceAssignments, global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
color_attachment_descriptor: &RenderPassColorAttachmentDescriptor, color_attachment_descriptor: &RenderPassColorAttachmentDescriptor,
) -> wgpu::RenderPassColorAttachmentDescriptor<'a> { ) -> wgpu::RenderPassColorAttachmentDescriptor<'a> {
let attachment = get_texture_view( let attachment = get_texture_view(
render_context, render_context,
global_render_resource_assignments, global_render_resource_assignments,
refs,
color_attachment_descriptor.attachment.as_str(), color_attachment_descriptor.attachment.as_str(),
); );
@ -521,6 +525,7 @@ fn create_wgpu_color_attachment_descriptor<'a>(
get_texture_view( get_texture_view(
render_context, render_context,
global_render_resource_assignments, global_render_resource_assignments,
refs,
target.as_str(), target.as_str(),
) )
}); });
@ -537,11 +542,13 @@ fn create_wgpu_color_attachment_descriptor<'a>(
fn create_wgpu_depth_stencil_attachment_descriptor<'a>( fn create_wgpu_depth_stencil_attachment_descriptor<'a>(
render_context: &'a WgpuRenderContext, render_context: &'a WgpuRenderContext,
global_render_resource_assignments: &RenderResourceAssignments, global_render_resource_assignments: &RenderResourceAssignments,
refs: &WgpuResourceRefs<'a>,
depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor, depth_stencil_attachment_descriptor: &RenderPassDepthStencilAttachmentDescriptor,
) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<'a> { ) -> wgpu::RenderPassDepthStencilAttachmentDescriptor<'a> {
let attachment = get_texture_view( let attachment = get_texture_view(
render_context, render_context,
global_render_resource_assignments, global_render_resource_assignments,
refs,
depth_stencil_attachment_descriptor.attachment.as_str(), depth_stencil_attachment_descriptor.attachment.as_str(),
); );

View File

@ -16,7 +16,7 @@ use bevy_window::{WindowId, Window};
#[derive(Clone)] #[derive(Clone)]
pub struct WgpuRenderResourceContext { pub struct WgpuRenderResourceContext {
pub device: Arc<wgpu::Device>, pub device: Arc<wgpu::Device>,
pub wgpu_resources: Arc<WgpuResources>, pub wgpu_resources: WgpuResources,
} }
@ -24,7 +24,7 @@ impl WgpuRenderResourceContext {
pub fn new(device: Arc<wgpu::Device>) -> Self { pub fn new(device: Arc<wgpu::Device>) -> Self {
WgpuRenderResourceContext { WgpuRenderResourceContext {
device, device,
wgpu_resources: Arc::new(WgpuResources::default()), wgpu_resources: WgpuResources::default(),
} }
} }
} }
@ -95,8 +95,8 @@ impl RenderResourceContext for WgpuRenderResourceContext {
.get_mesh_indices_resource(mesh) .get_mesh_indices_resource(mesh)
} }
fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> { fn get_resource_info(&self, resource: RenderResource, handle_info: &mut dyn FnMut(Option<&ResourceInfo>)) {
self.wgpu_resources.get_resource_info(resource) self.wgpu_resources.get_resource_info(resource, handle_info);
} }
fn asset_resources(&self) -> &AssetResources { fn asset_resources(&self) -> &AssetResources {

View File

@ -1,4 +1,4 @@
use crate::renderer_2::WgpuRenderContext; use crate::{renderer_2::WgpuRenderContext, WgpuResourceRefs};
use bevy_asset::Handle; use bevy_asset::Handle;
use bevy_render::{ use bevy_render::{
pass::RenderPass, pass::RenderPass,
@ -13,6 +13,7 @@ use std::{collections::HashMap, ops::Range};
pub struct WgpuRenderPass<'a> { pub struct WgpuRenderPass<'a> {
pub render_pass: wgpu::RenderPass<'a>, pub render_pass: wgpu::RenderPass<'a>,
pub render_context: &'a WgpuRenderContext, pub render_context: &'a WgpuRenderContext,
pub render_resources: WgpuResourceRefs<'a>,
pub bound_bind_groups: HashMap<u32, RenderResourceSetId>, pub bound_bind_groups: HashMap<u32, RenderResourceSetId>,
} }
@ -22,27 +23,21 @@ impl<'a> RenderPass for WgpuRenderPass<'a> {
} }
fn set_vertex_buffer(&mut self, start_slot: u32, resource: RenderResource, offset: u64) { fn set_vertex_buffer(&mut self, start_slot: u32, resource: RenderResource, offset: u64) {
let buffers = self let buffer = self
.render_context
.render_resources .render_resources
.wgpu_resources
.buffers .buffers
.read() .get(&resource)
.unwrap(); .unwrap();
let buffer = buffers.get(&resource).unwrap();
self.render_pass self.render_pass
.set_vertex_buffer(start_slot, &buffer, offset, 0); .set_vertex_buffer(start_slot, &buffer, offset, 0);
} }
fn set_index_buffer(&mut self, resource: RenderResource, offset: u64) { fn set_index_buffer(&mut self, resource: RenderResource, offset: u64) {
let buffers = self let buffer = self
.render_context
.render_resources .render_resources
.wgpu_resources
.buffers .buffers
.read() .get(&resource)
.unwrap(); .unwrap();
let buffer = buffers.get(&resource).unwrap();
self.render_pass.set_index_buffer(&buffer, offset, 0); self.render_pass.set_index_buffer(&buffer, offset, 0);
} }
@ -80,17 +75,15 @@ impl<'a> RenderPass for WgpuRenderPass<'a> {
index_buffer index_buffer
); );
self.set_index_buffer(index_buffer, 0); self.set_index_buffer(index_buffer, 0);
match self self.render_context.resources().get_resource_info(
.render_context index_buffer,
.resources() &mut |resource_info| match resource_info {
.get_resource_info(index_buffer) Some(ResourceInfo::Buffer(buffer_info)) => {
.unwrap() indices = Some(0..(buffer_info.size / 2) as u32)
{ }
ResourceInfo::Buffer(buffer_info) => { _ => panic!("expected a buffer type"),
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)) = if let Some((render_resource_set_id, dynamic_uniform_indices)) =
render_resource_assignments.get_render_resource_set_id(bind_group.id) render_resource_assignments.get_render_resource_set_id(bind_group.id)
{ {
if let Some(wgpu_bind_group) = self if let Some(bind_group_info) = self
.render_context
.render_resources .render_resources
.wgpu_resources .bind_groups
.get_bind_group(bind_group.id, *render_resource_set_id) .get(&bind_group.id)
{ {
const EMPTY: &'static [u32] = &[]; if let Some(wgpu_bind_group) =
let dynamic_uniform_indices = bind_group_info.bind_groups.get(render_resource_set_id)
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 *bound_render_resource_set == *render_resource_set_id const EMPTY: &'static [u32] = &[];
&& dynamic_uniform_indices.len() == 0 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 { if dynamic_uniform_indices.len() == 0 {
self.bound_bind_groups self.bound_bind_groups
.insert(bind_group.index, *render_resource_set_id); .insert(bind_group.index, *render_resource_set_id);
} else { } else {
self.bound_bind_groups.remove(&bind_group.index); self.bound_bind_groups.remove(&bind_group.index);
} }
log::trace!( log::trace!(
"set bind group {} {:?}: {:?}", "set bind group {} {:?}: {:?}",
bind_group.index, bind_group.index,
dynamic_uniform_indices, dynamic_uniform_indices,
render_resource_set_id render_resource_set_id
); );
self.render_pass.set_bind_group( self.render_pass.set_bind_group(
bind_group.index, bind_group.index,
&wgpu_bind_group, wgpu_bind_group,
dynamic_uniform_indices, dynamic_uniform_indices,
); );
}
}; };
} }
} }
@ -150,14 +146,7 @@ impl<'a> RenderPass for WgpuRenderPass<'a> {
indices indices
} }
fn set_pipeline(&mut self, pipeline_handle: Handle<PipelineDescriptor>) { fn set_pipeline(&mut self, pipeline_handle: Handle<PipelineDescriptor>) {
let render_pipelines = self let pipeline = self.render_resources.render_pipelines.get(&pipeline_handle).expect(
.render_context
.render_resources
.wgpu_resources
.render_pipelines
.read()
.unwrap();
let pipeline = render_pipelines.get(&pipeline_handle).expect(
"Attempted to use a pipeline that does not exist in this RenderPass's RenderContext", "Attempted to use a pipeline that does not exist in this RenderPass's RenderContext",
); );
self.render_pass.set_pipeline(pipeline); self.render_pass.set_pipeline(pipeline);

View File

@ -11,7 +11,7 @@ use bevy_render::{
}; };
use bevy_window::{WindowCreated, WindowResized, Windows}; use bevy_window::{WindowCreated, WindowResized, Windows};
use legion::prelude::*; 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 struct WgpuRenderer {
pub device: Arc<wgpu::Device>, pub device: Arc<wgpu::Device>,
@ -131,7 +131,6 @@ impl WgpuRenderer {
for resource_provider_chunk in render_graph.resource_providers.chunks_mut(chunk_size) { for resource_provider_chunk in render_graph.resource_providers.chunks_mut(chunk_size) {
// TODO: try to unify this Device usage // TODO: try to unify this Device usage
let device = self.device.clone(); let device = self.device.clone();
let resource_device = device.clone();
// let sender = sender.clone(); // let sender = sender.clone();
// s.spawn(|_| { // s.spawn(|_| {
// TODO: replace WgpuResources with Global+Local resources // TODO: replace WgpuResources with Global+Local resources
@ -241,17 +240,11 @@ impl WgpuRenderer {
let mut encoder = { let mut encoder = {
let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap(); let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap();
let mut any_context = (&mut global_context.context) as &mut dyn Any; let render_resource_context = global_context
// let mut any_context = (&mut global_context.context); .context
// println!("{}", std::any::type_name_of_val(any_context));
let mut render_resource_context = any_context
.downcast_mut::<WgpuRenderResourceContext>() .downcast_mut::<WgpuRenderResourceContext>()
.unwrap(); .unwrap();
panic!("ahh");
// let mut render_resource_context = any_context
// .downcast_mut::<WgpuRenderResourceContext>()
// .unwrap();
self.handle_window_created_events(resources, render_resource_context); self.handle_window_created_events(resources, render_resource_context);
self.handle_window_resized_events(resources, render_resource_context); self.handle_window_resized_events(resources, render_resource_context);
@ -266,11 +259,7 @@ impl WgpuRenderer {
self.intialized = true; self.intialized = true;
} }
self.update_resource_providers( self.update_resource_providers(world, resources, render_resource_context);
world,
resources,
render_resource_context,
);
update_shader_assignments(world, resources, &render_context); update_shader_assignments(world, resources, &render_context);
self.create_queued_textures(resources, &mut render_context.render_resources); 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 // TODO: add to POST_UPDATE and remove redundant global_context
render_resource_sets_system().run(world, resources); render_resource_sets_system().run(world, resources);
let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap(); let mut global_context = resources.get_mut::<GlobalRenderResourceContext>().unwrap();
let mut any_context = (&mut global_context.context) as &mut dyn Any; let render_resource_context = global_context
// let mut any_context = (&mut global_context.context); .context
// println!("{}", std::any::type_name_of_val(any_context));
let mut render_resource_context = any_context
.downcast_mut::<WgpuRenderResourceContext>() .downcast_mut::<WgpuRenderResourceContext>()
.unwrap(); .unwrap();
let mut render_context = let mut render_context =

View File

@ -20,12 +20,56 @@ pub struct WgpuBindGroupInfo {
pub bind_groups: HashMap<RenderResourceSetId, wgpu::BindGroup>, pub bind_groups: HashMap<RenderResourceSetId, wgpu::BindGroup>,
} }
/// 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 struct WgpuResourcesReadLock<'a> {
pub buffers: RwLockReadGuard<'a, HashMap<RenderResource, wgpu::Buffer>>, pub buffers: RwLockReadGuard<'a, HashMap<RenderResource, wgpu::Buffer>>,
pub textures: RwLockReadGuard<'a, HashMap<RenderResource, wgpu::TextureView>>, pub textures: RwLockReadGuard<'a, HashMap<RenderResource, wgpu::TextureView>>,
pub swap_chain_outputs: RwLockReadGuard<'a, HashMap<WindowId, wgpu::SwapChainOutput>>,
pub render_pipelines: RwLockReadGuard<'a, HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>>,
pub bind_groups: RwLockReadGuard<'a, HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>>,
} }
#[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<RenderResource, wgpu::Buffer>,
pub textures: &'a HashMap<RenderResource, wgpu::TextureView>,
pub swap_chain_outputs: &'a HashMap<WindowId, wgpu::SwapChainOutput>,
pub render_pipelines: &'a HashMap<Handle<PipelineDescriptor>, wgpu::RenderPipeline>,
pub bind_groups: &'a HashMap<BindGroupDescriptorId, WgpuBindGroupInfo>,
}
#[derive(Default, Clone)]
pub struct WgpuResources { pub struct WgpuResources {
// TODO: remove this from WgpuResources. it doesn't need to be here // TODO: remove this from WgpuResources. it doesn't need to be here
pub asset_resources: AssetResources, pub asset_resources: AssetResources,
@ -43,10 +87,13 @@ pub struct WgpuResources {
} }
impl WgpuResources { impl WgpuResources {
pub fn read(&self) -> WgpuResourcesReadLock { pub fn read<'a>(&'a self) -> WgpuResourcesReadLock<'a> {
WgpuResourcesReadLock { WgpuResourcesReadLock {
buffers: self.buffers.read().unwrap(), buffers: self.buffers.read().unwrap(),
textures: self.textures.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); .insert(resource, resource_info);
} }
pub fn get_bind_group( pub fn has_bind_group(
&self, &self,
bind_group_descriptor_id: BindGroupDescriptorId, bind_group_descriptor_id: BindGroupDescriptorId,
render_resource_set_id: RenderResourceSetId, render_resource_set_id: RenderResourceSetId,
) -> Option<&wgpu::BindGroup> { ) -> bool {
if let Some(bind_group_info) = self if let Some(bind_group_info) = self
.bind_groups .bind_groups
.read() .read()
.unwrap() .unwrap()
.get(&bind_group_descriptor_id) .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 { } else {
None false
} }
} }
@ -174,8 +221,11 @@ impl WgpuResources {
self.assign_buffer(buffer, buffer_info) self.assign_buffer(buffer, buffer_info)
} }
pub fn get_resource_info(&self, resource: RenderResource) -> Option<&ResourceInfo> { // TODO: taking a closure isn't fantastic. is there any way to make this better without exposing the lock in the interface?
self.resource_info.read().unwrap().get(&resource) 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) { pub fn remove_buffer(&self, resource: RenderResource) {