diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index d667e03159..ccc38c001d 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -1,8 +1,8 @@ use crate::{ pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineDescriptor}, render_resource::{ - RenderResourceAssignments, RenderResourceId, RenderResourceSet, RenderResourceSetId, - ResourceInfo, + BufferUsage, RenderResource, RenderResourceAssignment, RenderResourceAssignments, + RenderResourceId, RenderResourceSet, RenderResourceSetId, ResourceInfo, SharedBuffers, }, renderer::{RenderResourceContext, RenderResources}, }; @@ -13,6 +13,7 @@ use legion::{ storage::Component, }; use std::{ops::Range, sync::Arc}; +use thiserror::Error; #[derive(Debug, Clone, Eq, PartialEq)] pub enum RenderCommand { @@ -83,12 +84,15 @@ impl Draw { pipelines: &'a Assets, render_resource_context: &'a dyn RenderResourceContext, render_resource_assignments: &'a RenderResourceAssignments, + shared_buffers: &'a SharedBuffers, ) -> DrawContext { DrawContext { draw: self, pipelines, render_resource_context, render_resource_assignments, + shared_buffers, + current_pipeline: None, } } @@ -97,17 +101,59 @@ impl Draw { } } +#[derive(Debug, Error)] +pub enum DrawError { + #[error("Pipeline does not exist.")] + NonExistentPipeline, + #[error("No pipeline set")] + NoPipelineSet, + #[error("Pipeline has no layout")] + PipelineHasNoLayout, + #[error("Failed to get a buffer for the given RenderResource.")] + BufferAllocationFailure, +} + pub struct DrawContext<'a> { pub draw: &'a mut Draw, pub pipelines: &'a Assets, pub render_resource_context: &'a dyn RenderResourceContext, pub render_resource_assignments: &'a RenderResourceAssignments, + pub shared_buffers: &'a SharedBuffers, + pub current_pipeline: Option<&'a PipelineDescriptor>, } impl<'a> DrawContext<'a> { - pub fn set_pipeline(&mut self, pipeline: Handle) { - self.render_command(RenderCommand::SetPipeline { pipeline }); + pub fn get_uniform_buffer( + &self, + render_resource: &T, + ) -> Result { + self.get_buffer(render_resource, BufferUsage::UNIFORM) } + pub fn get_buffer( + &self, + render_resource: &T, + buffer_usage: BufferUsage, + ) -> Result { + self.shared_buffers + .get_buffer(render_resource, buffer_usage) + .ok_or_else(|| DrawError::BufferAllocationFailure) + } + + pub fn set_pipeline( + &mut self, + pipeline_handle: Handle, + ) -> Result<(), DrawError> { + let pipeline = self + .pipelines + .get(&pipeline_handle) + .ok_or_else(|| DrawError::NonExistentPipeline)?; + self.current_pipeline = Some(pipeline); + self.render_command(RenderCommand::SetPipeline { + pipeline: pipeline_handle, + }); + Ok(()) + } + pub fn set_vertex_buffer(&mut self, slot: u32, buffer: RenderResourceId, offset: u64) { self.render_command(RenderCommand::SetVertexBuffer { slot, @@ -146,21 +192,21 @@ impl<'a> DrawContext<'a> { self.draw.render_commands.push(render_command); } - pub fn draw(&mut self, drawable: &mut T) { - drawable.draw(self); + pub fn draw(&mut self, drawable: &mut T) -> Result<(), DrawError> { + drawable.draw(self) } } pub trait Drawable { - fn draw(&mut self, draw: &mut DrawContext); + fn draw(&mut self, draw: &mut DrawContext) -> Result<(), DrawError>; } impl Drawable for RenderPipelines { - fn draw(&mut self, draw: &mut DrawContext) { + fn draw(&mut self, draw: &mut DrawContext) -> Result<(), DrawError> { for pipeline_handle in self.compiled_pipelines.iter() { let pipeline = draw.pipelines.get(pipeline_handle).unwrap(); let layout = pipeline.get_layout().unwrap(); - draw.set_pipeline(*pipeline_handle); + draw.set_pipeline(*pipeline_handle)?; for bind_group in layout.bind_groups.iter() { if let Some(local_render_resource_set) = self .render_resource_assignments @@ -174,7 +220,6 @@ impl Drawable for RenderPipelines { draw.set_bind_group(bind_group, global_render_resource_set); } } - let mut indices = 0..0; for (slot, vertex_buffer_descriptor) in layout.vertex_buffer_descriptors.iter().enumerate() @@ -201,6 +246,8 @@ impl Drawable for RenderPipelines { draw.draw_indexed(indices, 0, 0..1); } + + Ok(()) } } @@ -208,12 +255,18 @@ pub fn draw_system( pipelines: Res>, render_resource_assignments: Res, render_resources: Res, + shared_buffers: Res, mut draw: ComMut, mut drawable: ComMut, ) { let context = &*render_resources.context; - let mut draw_context = draw.get_context(&pipelines, context, &render_resource_assignments); - draw_context.draw(drawable.as_mut()); + let mut draw_context = draw.get_context( + &pipelines, + context, + &render_resource_assignments, + &shared_buffers, + ); + draw_context.draw(drawable.as_mut()).unwrap(); } pub fn clear_draw_system(mut draw: ComMut) { diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 1814ac6e50..419f8c6d1b 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -1,4 +1,5 @@ mod buffer; +mod shared_buffer; mod render_resource; mod render_resource_set; mod render_resource_assignments; @@ -6,6 +7,7 @@ mod resource_info; mod systems; pub use buffer::*; +pub use shared_buffer::*; pub use render_resource::*; pub use render_resource_set::*; pub use render_resource_assignments::*; diff --git a/crates/bevy_render/src/render_resource/render_resource.rs b/crates/bevy_render/src/render_resource/render_resource.rs index 49159f7dd8..145875760c 100644 --- a/crates/bevy_render/src/render_resource/render_resource.rs +++ b/crates/bevy_render/src/render_resource/render_resource.rs @@ -95,6 +95,16 @@ impl_render_resource_bytes!(Vec2); impl_render_resource_bytes!(Vec3); impl_render_resource_bytes!(Vec4); impl_render_resource_bytes!(Mat4); +impl_render_resource_bytes!(u8); +impl_render_resource_bytes!(u16); +impl_render_resource_bytes!(u32); +impl_render_resource_bytes!(u64); +impl_render_resource_bytes!(i8); +impl_render_resource_bytes!(i16); +impl_render_resource_bytes!(i32); +impl_render_resource_bytes!(i64); +impl_render_resource_bytes!(f32); +impl_render_resource_bytes!(f64); impl RenderResource for Vec where diff --git a/crates/bevy_render/src/render_resource/render_resource_assignments.rs b/crates/bevy_render/src/render_resource/render_resource_assignments.rs index 755e263993..d2f176d670 100644 --- a/crates/bevy_render/src/render_resource/render_resource_assignments.rs +++ b/crates/bevy_render/src/render_resource/render_resource_assignments.rs @@ -59,6 +59,7 @@ pub enum RenderResourceSetStatus { // 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, Default)] pub struct RenderResourceAssignments { + // TODO: remove this. it shouldn't be needed anymore pub id: RenderResourceAssignmentsId, render_resources: HashMap, vertex_buffers: HashMap)>, diff --git a/crates/bevy_render/src/render_resource/shared_buffer.rs b/crates/bevy_render/src/render_resource/shared_buffer.rs new file mode 100644 index 0000000000..a22e46ce70 --- /dev/null +++ b/crates/bevy_render/src/render_resource/shared_buffer.rs @@ -0,0 +1,58 @@ +use super::{BufferInfo, RenderResource, RenderResourceAssignment, RenderResourceId}; +use crate::{render_resource::BufferUsage, renderer::RenderResourceContext}; +use legion::systems::Res; +use std::sync::{Arc, RwLock}; + +// TODO: Instead of allocating small "exact size" buffers each frame, this should use multiple large shared buffers and probably +// a long-living "cpu mapped" staging buffer. Im punting that for now because I don't know the best way to use wgpu's new async +// buffer mapping yet. +pub struct SharedBuffers { + render_resource_context: Box, + buffers: Arc>>, +} + +impl SharedBuffers { + pub fn new(render_resource_context: Box) -> Self { + Self { + render_resource_context, + buffers: Default::default(), + } + } + + pub fn get_buffer( + &self, + render_resource: &T, + buffer_usage: BufferUsage, + ) -> Option { + if let Some(size) = render_resource.buffer_byte_len() { + // PERF: this buffer will be slow + let buffer = self.render_resource_context.create_buffer_mapped( + BufferInfo { size, buffer_usage: buffer_usage | BufferUsage::COPY_SRC | BufferUsage::COPY_DST }, + &mut |data, _renderer| { + render_resource.write_buffer_bytes(data); + }, + ); + self.buffers.write().unwrap().push(buffer); + Some(RenderResourceAssignment::Buffer { + resource: buffer, + range: 0..size as u64, + dynamic_index: None, + }) + } else { + None + } + } + + // TODO: remove this when this actually uses shared buffers + pub fn free_buffers(&self) { + let mut buffers = self.buffers.write().unwrap(); + for buffer in buffers.drain(..) { + self.render_resource_context.remove_buffer(buffer) + } + } +} + +// TODO: remove this when this actually uses shared buffers +pub fn free_shared_buffers_system(shared_buffers: Res) { + shared_buffers.free_buffers(); +} diff --git a/crates/bevy_render/src/texture/texture.rs b/crates/bevy_render/src/texture/texture.rs index 806fb1e168..bc53ce75e9 100644 --- a/crates/bevy_render/src/texture/texture.rs +++ b/crates/bevy_render/src/texture/texture.rs @@ -12,7 +12,7 @@ use std::collections::HashSet; pub const TEXTURE_ASSET_INDEX: usize = 0; pub const SAMPLER_ASSET_INDEX: usize = 1; -#[derive(Default)] +#[derive(Default, Clone)] pub struct Texture { pub data: Vec, pub size: Vec2,