From 3d07fbdc81a8b0d4665bed46de928cc8aeb539b1 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 9 Jun 2020 23:16:48 -0700 Subject: [PATCH] render: "Immediate Mode" draw api This replaces Renderable with Draw/RenderPipelines components and makes various aspects of the renderer much simpler and legible --- crates/bevy_pbr/src/entity.rs | 5 +- .../bevy_pbr/src/forward_pbr_render_graph.rs | 9 +- .../bevy_render/src/base_render_graph/mod.rs | 4 +- crates/bevy_render/src/draw.rs | 230 +++++++++++++++-- crates/bevy_render/src/entity.rs | 5 +- crates/bevy_render/src/lib.rs | 26 +- crates/bevy_render/src/mesh.rs | 21 +- crates/bevy_render/src/pass/render_pass.rs | 10 +- crates/bevy_render/src/pipeline/pipeline.rs | 31 +-- .../src/pipeline/pipeline_compiler.rs | 151 ++++++----- .../src/render_graph/nodes/pass_node.rs | 242 ++++-------------- .../src/render_graph/nodes/uniform_node.rs | 200 +++++++-------- .../entity_render_resource_assignments.rs | 31 --- crates/bevy_render/src/render_resource/mod.rs | 4 +- .../render_resource_assignments.rs | 134 +++++++--- .../src/render_resource/systems.rs | 68 +++++ crates/bevy_render/src/renderable.rs | 32 --- .../headless_render_resource_context.rs | 19 +- .../src/renderer/render_resource_context.rs | 30 +-- crates/bevy_render/src/shader/shader_defs.rs | 24 +- crates/bevy_sprite/src/entity.rs | 14 +- crates/bevy_sprite/src/render/mod.rs | 7 +- crates/bevy_ui/src/entity.rs | 14 +- crates/bevy_ui/src/render/mod.rs | 9 +- crates/bevy_wgpu/src/lib.rs | 12 +- crates/bevy_wgpu/src/renderer/mod.rs | 2 - crates/bevy_wgpu/src/renderer/systems.rs | 92 ------- .../renderer/wgpu_render_resource_context.rs | 143 +++++------ crates/bevy_wgpu/src/wgpu_render_pass.rs | 26 +- crates/bevy_wgpu/src/wgpu_renderer.rs | 26 +- examples/ecs/ecs_guide.rs | 4 +- examples/shader/shader_custom_material.rs | 4 +- examples/shader/shader_defs.rs | 6 +- src/prelude.rs | 5 +- 34 files changed, 767 insertions(+), 873 deletions(-) delete mode 100644 crates/bevy_render/src/render_resource/entity_render_resource_assignments.rs create mode 100644 crates/bevy_render/src/render_resource/systems.rs delete mode 100644 crates/bevy_render/src/renderable.rs delete mode 100644 crates/bevy_wgpu/src/renderer/systems.rs diff --git a/crates/bevy_pbr/src/entity.rs b/crates/bevy_pbr/src/entity.rs index 3b4db92313..281f3e723f 100644 --- a/crates/bevy_pbr/src/entity.rs +++ b/crates/bevy_pbr/src/entity.rs @@ -1,7 +1,7 @@ use crate::{light::Light, material::StandardMaterial}; use bevy_asset::Handle; use bevy_derive::EntityArchetype; -use bevy_render::{mesh::Mesh, Renderable}; +use bevy_render::{mesh::Mesh, draw::{RenderPipelines, Draw}}; use bevy_transform::prelude::{Transform, Rotation, Scale, Translation}; #[derive(EntityArchetype, Default)] @@ -10,7 +10,8 @@ pub struct MeshEntity { pub mesh: Handle, // #[tag] pub material: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, pub transform: Transform, pub translation: Translation, pub rotation: Rotation, diff --git a/crates/bevy_pbr/src/forward_pbr_render_graph.rs b/crates/bevy_pbr/src/forward_pbr_render_graph.rs index a25dbf44cf..e6c3a3f36a 100644 --- a/crates/bevy_pbr/src/forward_pbr_render_graph.rs +++ b/crates/bevy_pbr/src/forward_pbr_render_graph.rs @@ -4,7 +4,7 @@ use bevy_render::{ base_render_graph, pipeline::PipelineDescriptor, render_graph::{ - nodes::{AssetUniformNode, PassNode, UniformNode}, + nodes::{AssetUniformNode, UniformNode}, RenderGraph, }, shader::Shader, @@ -36,12 +36,7 @@ impl ForwardPbrRenderGraphBuilder for RenderGraph { self.add_system_node(node::LIGHTS, LightsNode::new(10)); let mut shaders = resources.get_mut::>().unwrap(); let mut pipelines = resources.get_mut::>().unwrap(); - { - let main_pass: &mut PassNode = self - .get_node_mut(base_render_graph::node::MAIN_PASS) - .unwrap(); - main_pass.add_pipeline(pipelines.add_default(build_forward_pipeline(&mut shaders))); - } + pipelines.add_default(build_forward_pipeline(&mut shaders)); // TODO: replace these with "autowire" groups self.add_node_edge(node::STANDARD_MATERIAL, base_render_graph::node::MAIN_PASS) diff --git a/crates/bevy_render/src/base_render_graph/mod.rs b/crates/bevy_render/src/base_render_graph/mod.rs index 72576f8b08..0af075869d 100644 --- a/crates/bevy_render/src/base_render_graph/mod.rs +++ b/crates/bevy_render/src/base_render_graph/mod.rs @@ -4,7 +4,7 @@ use crate::{ RenderPassDepthStencilAttachmentDescriptor, StoreOp, TextureAttachment, }, render_graph::{ - nodes::{CameraNode, PassNode, TextureCopyNode, WindowSwapChainNode, WindowTextureNode}, + nodes::{CameraNode, MainPassNode, TextureCopyNode, WindowSwapChainNode, WindowTextureNode}, RenderGraph, }, texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage}, @@ -95,7 +95,7 @@ impl BaseRenderGraphBuilder for RenderGraph { if config.add_main_pass { self.add_node( node::MAIN_PASS, - PassNode::new(PassDescriptor { + MainPassNode::new(PassDescriptor { color_attachments: vec![RenderPassColorAttachmentDescriptor { attachment: TextureAttachment::Input("color".to_string()), resolve_target: None, diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 0aaa64cda7..dddbcf6473 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -1,41 +1,221 @@ -use crate::{render_resource::RenderResourceId, pipeline::PipelineDescriptor}; -use bevy_asset::Handle; -use std::ops::Range; +use crate::{ + pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineDescriptor}, + render_resource::{ + RenderResourceAssignments, RenderResourceId, RenderResourceSet, RenderResourceSetId, + ResourceInfo, + }, + renderer::{RenderResourceContext, RenderResources}, +}; +use bevy_asset::{Assets, Handle}; +use bevy_property::Properties; +use legion::{ + prelude::{Com, ComMut, Res}, + storage::Component, +}; +use std::{ops::Range, sync::Arc}; #[derive(Debug, Clone, Eq, PartialEq)] -pub enum DrawType { - Instanced { +pub enum RenderCommand { + SetPipeline { + pipeline: Handle, + }, + SetVertexBuffer { + slot: u32, + buffer: RenderResourceId, + offset: u64, + }, + SetIndexBuffer { + buffer: RenderResourceId, + offset: u64, + }, + SetBindGroup { + index: u32, + bind_group_descriptor: BindGroupDescriptorId, + render_resource_set: RenderResourceSetId, + dynamic_uniform_indices: Option>>, + }, + DrawIndexed { indices: Range, base_vertex: i32, instances: Range, + }, +} + +#[derive(Properties)] +pub struct Draw { + pub is_visible: bool, + #[property(ignore)] + pub render_commands: Vec, +} + +impl Default for Draw { + fn default() -> Self { + Self { + is_visible: true, + render_commands: Default::default(), + } } } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct VertexBufferBinding { - pub slot: u32, - pub vertex_buffer: RenderResourceId, - pub offset: u64, +#[derive(Properties)] +pub struct RenderPipelines { + pub pipelines: Vec>, + // TODO: make these pipeline specific + #[property(ignore)] + pub render_resource_assignments: RenderResourceAssignments, + #[property(ignore)] + pub compiled_pipelines: Vec>, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct IndexBufferBinding { - pub vertex_buffer: RenderResourceId, - pub offset: u64, +impl Default for RenderPipelines { + fn default() -> Self { + Self { + render_resource_assignments: Default::default(), + compiled_pipelines: Default::default(), + pipelines: vec![Handle::default()], + } + } } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct BindGroupBinding { - pub vertex_buffer: RenderResourceId, - pub offset: u64, +impl Draw { + pub fn get_context<'a>( + &'a mut self, + pipelines: &'a Assets, + render_resource_context: &'a dyn RenderResourceContext, + render_resource_assignments: &'a RenderResourceAssignments, + ) -> DrawContext { + DrawContext { + draw: self, + pipelines, + render_resource_context, + render_resource_assignments, + } + } + + pub fn clear_render_commands(&mut self) { + self.render_commands.clear(); + } } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct DrawCall { - pub pipeline: Handle, - pub draw_type: DrawType, - pub vertex_buffers: Vec, - pub index_buffer: Option, +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 struct Draw {} +impl<'a> DrawContext<'a> { + pub fn set_pipeline(&mut self, pipeline: Handle) { + self.render_command(RenderCommand::SetPipeline { pipeline }); + } + pub fn set_vertex_buffer(&mut self, slot: u32, buffer: RenderResourceId, offset: u64) { + self.render_command(RenderCommand::SetVertexBuffer { + slot, + buffer, + offset, + }); + } + + pub fn set_index_buffer(&mut self, buffer: RenderResourceId, offset: u64) { + self.render_command(RenderCommand::SetIndexBuffer { buffer, offset }); + } + + pub fn set_bind_group( + &mut self, + bind_group_descriptor: &BindGroupDescriptor, + render_resource_set: &RenderResourceSet, + ) { + self.render_command(RenderCommand::SetBindGroup { + index: bind_group_descriptor.index, + bind_group_descriptor: bind_group_descriptor.id, + render_resource_set: render_resource_set.id, + dynamic_uniform_indices: render_resource_set.dynamic_uniform_indices.clone(), + }); + } + + pub fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { + self.render_command(RenderCommand::DrawIndexed { + base_vertex, + indices, + instances, + }); + } + + #[inline] + pub fn render_command(&mut self, render_command: RenderCommand) { + self.draw.render_commands.push(render_command); + } + + pub fn draw(&mut self, drawable: &T) { + drawable.draw(self); + } +} + +pub trait Drawable { + fn draw(&self, draw: &mut DrawContext); +} + +impl Drawable for RenderPipelines { + fn draw(&self, draw: &mut DrawContext) { + 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); + for bind_group in layout.bind_groups.iter() { + if let Some(local_render_resource_set) = self + .render_resource_assignments + .get_bind_group_render_resource_set(bind_group.id) + { + draw.set_bind_group(bind_group, local_render_resource_set); + } else if let Some(global_render_resource_set) = draw + .render_resource_assignments + .get_bind_group_render_resource_set(bind_group.id) + { + 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() + { + if let Some((vertex_buffer, index_buffer)) = self + .render_resource_assignments + .get_vertex_buffer(&vertex_buffer_descriptor.name) + { + draw.set_vertex_buffer(slot as u32, vertex_buffer, 0); + if let Some(index_buffer) = index_buffer { + draw.render_resource_context.get_resource_info( + index_buffer, + &mut |resource_info| match resource_info { + Some(ResourceInfo::Buffer(Some(buffer_info))) => { + indices = 0..(buffer_info.size / 2) as u32; + } + _ => panic!("expected a buffer type"), + }, + ); + draw.set_index_buffer(index_buffer, 0); + } + } + } + + draw.draw_indexed(indices, 0, 0..1); + } + } +} + +pub fn draw_system( + pipelines: Res>, + render_resource_assignments: Res, + render_resources: Res, + mut draw: ComMut, + drawable: Com, +) { + let context = &*render_resources.context; + let mut draw_context = draw.get_context(&pipelines, context, &render_resource_assignments); + draw_context.draw(drawable.as_ref()); +} + +pub fn clear_draw_system(mut draw: ComMut) { + draw.clear_render_commands(); +} diff --git a/crates/bevy_render/src/entity.rs b/crates/bevy_render/src/entity.rs index 89774c9254..8bbc70f78d 100644 --- a/crates/bevy_render/src/entity.rs +++ b/crates/bevy_render/src/entity.rs @@ -1,6 +1,6 @@ use crate::{ base_render_graph, mesh::Mesh, Camera, OrthographicProjection, PerspectiveProjection, - Renderable, WindowOrigin, + RenderPipelines, WindowOrigin, draw::Draw, }; use bevy_asset::Handle; use bevy_derive::EntityArchetype; @@ -10,7 +10,8 @@ use bevy_transform::components::{Transform, Rotation, Scale, Translation}; pub struct MeshMaterialEntity { pub mesh: Handle, pub material: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, pub transform: Transform, pub translation: Translation, pub rotation: Rotation, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 4f7264b3b3..62ddf1dc19 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -12,7 +12,6 @@ mod color; pub use camera::*; pub use color::*; -pub use renderable::*; pub use vertex::Vertex; @@ -20,20 +19,14 @@ pub mod base_render_graph; pub mod pass; pub mod pipeline; pub mod render_resource; -mod renderable; pub mod texture; pub use once_cell; use self::{ mesh::Mesh, - pipeline::{ - PipelineAssignments, PipelineCompiler, PipelineDescriptor, VertexBufferDescriptors, - }, - render_resource::{ - entity_render_resource_assignments_system, EntityRenderResourceAssignments, - RenderResourceAssignments, - }, + pipeline::{PipelineCompiler, PipelineDescriptor, VertexBufferDescriptors}, + render_resource::RenderResourceAssignments, shader::Shader, texture::Texture, }; @@ -42,6 +35,7 @@ use base_render_graph::{BaseRenderGraphBuilder, BaseRenderGraphConfig}; use bevy_app::{AppBuilder, AppPlugin}; use bevy_asset::AddAsset; use bevy_type_registry::RegisterType; +use draw::{clear_draw_system, Draw, RenderPipelines}; use legion::prelude::IntoSystem; use mesh::mesh_resource_provider_system; use render_graph::RenderGraph; @@ -51,6 +45,7 @@ use texture::{PngTextureLoader, TextureResourceSystemState}; pub mod stage { pub static RENDER_RESOURCE: &str = "render_resource"; + pub static PRE_RENDER: &str = "pre_render"; pub static RENDER: &str = "render"; } @@ -70,30 +65,27 @@ impl Default for RenderPlugin { impl AppPlugin for RenderPlugin { fn build(&self, app: &mut AppBuilder) { app.add_stage_after(bevy_asset::stage::ASSET_EVENTS, stage::RENDER_RESOURCE) - .add_stage_after(stage::RENDER_RESOURCE, stage::RENDER) + .add_stage_after(stage::RENDER_RESOURCE, stage::PRE_RENDER) + .add_stage_after(stage::PRE_RENDER, stage::RENDER) .add_asset::() .add_asset::() .add_asset::() .add_asset::() .add_asset_loader::() .register_component::() + .register_component::() + .register_component::() .register_component::() .register_component::() - .register_component::() .register_property_type::() .register_property_type::>() .init_resource::() - .init_resource::() .init_resource::() .init_resource::() .init_resource::() - .init_resource::() .init_resource::() .init_resource::() - .add_system_to_stage( - bevy_app::stage::POST_UPDATE, - entity_render_resource_assignments_system(), - ) + .add_system_to_stage(bevy_app::stage::PRE_UPDATE, clear_draw_system.system()) .init_system_to_stage( bevy_app::stage::POST_UPDATE, camera::camera_system::, diff --git a/crates/bevy_render/src/mesh.rs b/crates/bevy_render/src/mesh.rs index 42137f4499..15ed416545 100644 --- a/crates/bevy_render/src/mesh.rs +++ b/crates/bevy_render/src/mesh.rs @@ -1,12 +1,11 @@ use crate::{ pipeline::{ state_descriptors::{IndexFormat, PrimitiveTopology}, - VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat, - AsVertexBufferDescriptor, + AsVertexBufferDescriptor, VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat, }, render_resource::{BufferInfo, BufferUsage}, renderer::{RenderResourceContext, RenderResources}, - Renderable, Vertex, + RenderPipelines, Vertex, }; use bevy_app::{EventReader, Events}; use bevy_asset::{AssetEvent, Assets, Handle}; @@ -345,7 +344,7 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box, meshes: Res>, mesh_events: Res>>, - query: &mut Query<(Read>, Write)>| { + query: &mut Query<(Read>, Write)>| { let render_resources = &*render_resources.context; let mut changed_meshes = HashSet::new(); for event in mesh_event_reader.iter(&mesh_events) { @@ -403,9 +402,9 @@ pub fn mesh_resource_provider_system(resources: &mut Resources) -> Box Box Box, base_vertex: i32, instances: Range); fn set_bind_group( &mut self, - bind_group_descriptor: &BindGroupDescriptor, - render_resource_set: &RenderResourceSet, + index: u32, + bind_group_descriptor: BindGroupDescriptorId, + render_resource_set: RenderResourceSetId, + dynamic_uniform_indices: Option<&[u32]>, ); } diff --git a/crates/bevy_render/src/pipeline/pipeline.rs b/crates/bevy_render/src/pipeline/pipeline.rs index 1aed1ebe10..e55e654b80 100644 --- a/crates/bevy_render/src/pipeline/pipeline.rs +++ b/crates/bevy_render/src/pipeline/pipeline.rs @@ -13,23 +13,10 @@ use crate::{ }; use bevy_asset::Assets; -// TODO: consider removing this in favor of Option -#[derive(Clone, Debug)] -pub enum PipelineLayoutType { - Manual(PipelineLayout), - Reflected(Option), -} - -#[derive(Clone, Debug)] -pub enum DescriptorType { - Manual(T), - Reflected(Option), -} - #[derive(Clone, Debug)] pub struct PipelineDescriptor { pub name: Option, - pub layout: PipelineLayoutType, + pub layout: Option, pub shader_stages: ShaderStages, pub rasterization_state: Option, @@ -63,7 +50,7 @@ impl PipelineDescriptor { pub fn new(shader_stages: ShaderStages) -> Self { PipelineDescriptor { name: None, - layout: PipelineLayoutType::Reflected(None), + layout: None, color_states: Vec::new(), depth_stencil_state: None, shader_stages, @@ -80,7 +67,7 @@ impl PipelineDescriptor { PipelineDescriptor { name: None, primitive_topology: PrimitiveTopology::TriangleList, - layout: PipelineLayoutType::Reflected(None), + layout: None, index_format: IndexFormat::Uint16, sample_count: 1, sample_mask: !0, @@ -120,17 +107,11 @@ impl PipelineDescriptor { } pub fn get_layout(&self) -> Option<&PipelineLayout> { - match self.layout { - PipelineLayoutType::Reflected(ref layout) => layout.as_ref(), - PipelineLayoutType::Manual(ref layout) => Some(layout), - } + self.layout.as_ref() } pub fn get_layout_mut(&mut self) -> Option<&mut PipelineLayout> { - match self.layout { - PipelineLayoutType::Reflected(ref mut layout) => layout.as_mut(), - PipelineLayoutType::Manual(ref mut layout) => Some(layout), - } + self.layout.as_mut() } /// Reflects the pipeline layout from its shaders. @@ -190,6 +171,6 @@ impl PipelineDescriptor { } } - self.layout = PipelineLayoutType::Reflected(Some(layout)); + self.layout = Some(layout); } } diff --git a/crates/bevy_render/src/pipeline/pipeline_compiler.rs b/crates/bevy_render/src/pipeline/pipeline_compiler.rs index a4025e3079..b83e17c1b7 100644 --- a/crates/bevy_render/src/pipeline/pipeline_compiler.rs +++ b/crates/bevy_render/src/pipeline/pipeline_compiler.rs @@ -1,8 +1,8 @@ use super::{state_descriptors::PrimitiveTopology, PipelineDescriptor, VertexBufferDescriptors}; use crate::{ - render_resource::{RenderResourceAssignments, RenderResourceAssignmentsId}, + draw::RenderPipelines, + renderer::{RenderResourceContext, RenderResources}, shader::{Shader, ShaderSource}, - Renderable, }; use bevy_asset::{Assets, Handle}; use std::collections::{HashMap, HashSet}; @@ -78,14 +78,15 @@ impl PipelineCompiler { vertex_buffer_descriptors: &VertexBufferDescriptors, shaders: &mut Assets, pipeline_descriptor: &PipelineDescriptor, - render_resource_assignments: &RenderResourceAssignments, + render_pipelines: &RenderPipelines, ) -> PipelineDescriptor { let mut compiled_pipeline_descriptor = pipeline_descriptor.clone(); compiled_pipeline_descriptor.shader_stages.vertex = self.compile_shader( shaders, &pipeline_descriptor.shader_stages.vertex, - &render_resource_assignments + &render_pipelines + .render_resource_assignments .pipeline_specialization .shader_specialization, ); @@ -97,7 +98,8 @@ impl PipelineCompiler { self.compile_shader( shaders, fragment, - &render_resource_assignments + &render_pipelines + .render_resource_assignments .pipeline_specialization .shader_specialization, ) @@ -107,72 +109,78 @@ impl PipelineCompiler { shaders, true, Some(vertex_buffer_descriptors), - Some(render_resource_assignments), + Some(&render_pipelines.render_resource_assignments), ); - compiled_pipeline_descriptor.primitive_topology = render_resource_assignments + compiled_pipeline_descriptor.primitive_topology = render_pipelines + .render_resource_assignments .pipeline_specialization .primitive_topology; compiled_pipeline_descriptor } - fn update_shader_assignments( + fn compile_pipelines( &mut self, vertex_buffer_descriptors: &VertexBufferDescriptors, - shader_pipeline_assignments: &mut PipelineAssignments, pipelines: &mut Assets, shaders: &mut Assets, - pipeline_handles: &[Handle], - render_resource_assignments: &RenderResourceAssignments, + render_pipelines: &mut RenderPipelines, + render_resource_context: &dyn RenderResourceContext, ) { - for pipeline_handle in pipeline_handles.iter() { + for (i, pipeline_handle) in render_pipelines.pipelines.iter().enumerate() { if let None = self.pipeline_source_to_compiled.get(pipeline_handle) { self.pipeline_source_to_compiled .insert(*pipeline_handle, Vec::new()); } - let final_handle = if let Some((_shader_defs, macroed_pipeline_handle)) = self - .pipeline_source_to_compiled - .get_mut(pipeline_handle) - .unwrap() - .iter() - .find(|(pipeline_specialization, _macroed_pipeline_handle)| { - *pipeline_specialization == render_resource_assignments.pipeline_specialization - }) { - *macroed_pipeline_handle + let compiled_pipeline_handle = if let Some((_shader_defs, compiled_pipeline_handle)) = + self.pipeline_source_to_compiled + .get_mut(pipeline_handle) + .unwrap() + .iter() + .find(|(pipeline_specialization, _compiled_pipeline_handle)| { + *pipeline_specialization + == render_pipelines + .render_resource_assignments + .pipeline_specialization + }) { + *compiled_pipeline_handle } else { let pipeline_descriptor = pipelines.get(pipeline_handle).unwrap(); - let compiled_pipeline = self.compile_pipeline( + let compiled_pipeline_descriptor = self.compile_pipeline( vertex_buffer_descriptors, shaders, pipeline_descriptor, - render_resource_assignments, + render_pipelines, + ); + let compiled_pipeline_handle = pipelines.add(compiled_pipeline_descriptor); + render_resource_context.create_render_pipeline( + compiled_pipeline_handle, + pipelines.get(&compiled_pipeline_handle).unwrap(), + &shaders, ); - let compiled_pipeline_handle = pipelines.add(compiled_pipeline); - let macro_pipelines = self + let compiled_pipelines = self .pipeline_source_to_compiled .get_mut(pipeline_handle) .unwrap(); - macro_pipelines.push(( - render_resource_assignments.pipeline_specialization.clone(), + compiled_pipelines.push(( + render_pipelines + .render_resource_assignments + .pipeline_specialization + .clone(), compiled_pipeline_handle, )); compiled_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()); + if i == render_pipelines.compiled_pipelines.len() { + render_pipelines + .compiled_pipelines + .push(compiled_pipeline_handle); + } else { + render_pipelines.compiled_pipelines[i] = compiled_pipeline_handle; } - - let assignments = shader_pipeline_assignments - .assignments - .get_mut(&final_handle) - .unwrap(); - assignments.push(render_resource_assignments.id); } } @@ -199,49 +207,34 @@ impl PipelineCompiler { } } -#[derive(Default)] -pub struct PipelineAssignments { - pub assignments: HashMap, Vec>, -} - // TODO: make this a system -pub fn update_shader_assignments(world: &mut World, resources: &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 - { - let mut shader_pipeline_assignments = resources.get_mut::().unwrap(); - let mut pipeline_compiler = resources.get_mut::().unwrap(); - let mut shaders = resources.get_mut::>().unwrap(); - let vertex_buffer_descriptors = resources.get::().unwrap(); - let mut pipeline_descriptor_storage = - resources.get_mut::>().unwrap(); +pub fn compile_pipelines_system( + world: &mut SubWorld, + mut pipeline_compiler: ResMut, + mut shaders: ResMut>, + mut pipelines: ResMut>, + vertex_buffer_descriptors: Res, + render_resources: Res, + query: &mut Query>, +) { + let render_resource_context = &*render_resources.context; - // reset assignments so they are updated every frame - shader_pipeline_assignments.assignments = HashMap::new(); + // TODO: only update when RenderPipelines is changed + for mut render_pipelines in query.iter_mut(world) { + pipeline_compiler.compile_pipelines( + &vertex_buffer_descriptors, + &mut pipelines, + &mut shaders, + &mut render_pipelines, + render_resource_context, + ); - // 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 { - continue; - } - - pipeline_compiler.update_shader_assignments( - &vertex_buffer_descriptors, - &mut shader_pipeline_assignments, - &mut pipeline_descriptor_storage, - &mut shaders, - &renderable.pipelines, - &renderable.render_resource_assignments, - ); - - // reset shader_defs so they can be changed next frame - renderable - .render_resource_assignments - .pipeline_specialization - .shader_specialization - .shader_defs - .clear(); - } + // reset shader_defs so they can be changed next frame + render_pipelines + .render_resource_assignments + .pipeline_specialization + .shader_specialization + .shader_defs + .clear(); } } diff --git a/crates/bevy_render/src/render_graph/nodes/pass_node.rs b/crates/bevy_render/src/render_graph/nodes/pass_node.rs index a78b0c56de..208dd8e3ac 100644 --- a/crates/bevy_render/src/render_graph/nodes/pass_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/pass_node.rs @@ -1,28 +1,20 @@ use crate::{ - pass::{PassDescriptor, RenderPass, TextureAttachment}, - pipeline::{PipelineAssignments, PipelineCompiler, PipelineDescriptor}, + draw::{Draw, RenderCommand}, + pass::{PassDescriptor, TextureAttachment}, render_graph::{Node, ResourceSlotInfo, ResourceSlots}, - render_resource::{ - EntitiesWaitingForAssets, EntityRenderResourceAssignments, RenderResourceAssignments, - ResourceInfo, - }, + render_resource::{EntitiesWaitingForAssets, RenderResourceAssignments, ResourceInfo}, renderer::RenderContext, - shader::Shader, - Renderable, }; -use bevy_asset::{Assets, Handle}; use legion::prelude::*; -use std::ops::Range; -pub struct PassNode { +pub struct MainPassNode { descriptor: PassDescriptor, - pipelines: Vec>, inputs: Vec, color_attachment_input_indices: Vec>, depth_stencil_attachment_input_index: Option, } -impl PassNode { +impl MainPassNode { pub fn new(descriptor: PassDescriptor) -> Self { let mut inputs = Vec::new(); let mut color_attachment_input_indices = Vec::new(); @@ -49,77 +41,16 @@ impl PassNode { } } - PassNode { + MainPassNode { descriptor, - pipelines: Vec::new(), inputs, color_attachment_input_indices, depth_stencil_attachment_input_index, } } - - pub fn add_pipeline(&mut self, pipeline_handle: Handle) { - self.pipelines.push(pipeline_handle); - } - - fn set_render_resources( - render_pass: &mut dyn RenderPass, - pipeline_descriptor: &PipelineDescriptor, - render_resource_assignments: &RenderResourceAssignments, - ) -> Option> { - let pipeline_layout = pipeline_descriptor.get_layout().unwrap(); - // PERF: vertex buffer lookup comes at a cost when vertex buffers aren't in render_resource_assignments. iterating over render_resource_assignment vertex buffers - // would likely be faster - let mut indices = None; - for (i, vertex_buffer_descriptor) in - pipeline_layout.vertex_buffer_descriptors.iter().enumerate() - { - if let Some((vertex_buffer, index_buffer)) = - render_resource_assignments.get_vertex_buffer(&vertex_buffer_descriptor.name) - { - log::trace!( - "set vertex buffer {}: {} ({:?})", - i, - vertex_buffer_descriptor.name, - vertex_buffer - ); - render_pass.set_vertex_buffer(i as u32, vertex_buffer, 0); - if let Some(index_buffer) = index_buffer { - log::trace!( - "set index buffer: {} ({:?})", - vertex_buffer_descriptor.name, - index_buffer - ); - render_pass.set_index_buffer(index_buffer, 0); - render_pass - .get_render_context() - .resources() - .get_resource_info( - index_buffer, - &mut |resource_info| match resource_info { - Some(ResourceInfo::Buffer(Some(buffer_info))) => { - indices = Some(0..(buffer_info.size / 2) as u32) - } - _ => panic!("expected a buffer type"), - }, - ); - } - } - } - - for bind_group in pipeline_layout.bind_groups.iter() { - if let Some(render_resource_set) = - render_resource_assignments.get_render_resource_set(bind_group.id) - { - render_pass.set_bind_group(bind_group, &render_resource_set); - } - } - - indices - } } -impl Node for PassNode { +impl Node for MainPassNode { fn input(&self) -> &[ResourceSlotInfo] { &self.inputs } @@ -132,14 +63,8 @@ impl Node for PassNode { input: &ResourceSlots, _output: &mut ResourceSlots, ) { - let pipeline_compiler = resources.get::().unwrap(); - let pipelines = resources.get::>().unwrap(); - let shader_pipeline_assignments = resources.get::().unwrap(); - let entity_render_resource_assignments = - resources.get::().unwrap(); let entities_waiting_for_assets = resources.get::().unwrap(); - let mut render_resource_assignments = - resources.get_mut::().unwrap(); + let render_resource_assignments = resources.get::().unwrap(); for (i, color_attachment) in self.descriptor.color_attachments.iter_mut().enumerate() { if let Some(input_index) = self.color_attachment_input_indices[i] { @@ -156,117 +81,56 @@ impl Node for PassNode { .attachment = TextureAttachment::RenderResource(input.get(input_index).unwrap()); } - { - let render_resource_context = render_context.resources(); - - // TODO: try merging the two pipeline loops below - let shaders = resources.get::>().unwrap(); - for pipeline_handle in self.pipelines.iter() { - if let Some(compiled_pipelines_iter) = - pipeline_compiler.iter_compiled_pipelines(*pipeline_handle) - { - for compiled_pipeline_handle in compiled_pipelines_iter { - let compiled_pipeline_descriptor = - pipelines.get(compiled_pipeline_handle).unwrap(); - - let pipeline_layout = compiled_pipeline_descriptor.get_layout().unwrap(); - { - // TODO: this breaks down in a parallel setting. it needs to change. ideally in a way that - // doesn't require modifying RenderResourceAssignments - for bind_group in pipeline_layout.bind_groups.iter() { - render_resource_assignments - .update_render_resource_set_id(bind_group); - } - } - - render_resource_context.create_render_pipeline( - *compiled_pipeline_handle, - &compiled_pipeline_descriptor, - &shaders, - ); - - render_resource_context.setup_bind_groups( - compiled_pipeline_descriptor, - &render_resource_assignments, - ); - let assigned_render_resource_assignments = shader_pipeline_assignments - .assignments - .get(&compiled_pipeline_handle); - if let Some(assigned_render_resource_assignments) = - assigned_render_resource_assignments - { - for assignment_id in assigned_render_resource_assignments.iter() { - 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; - } - - render_resource_context.setup_bind_groups( - compiled_pipeline_descriptor, - &renderable.render_resource_assignments, - ); - } - } - } - } - } - } - render_context.begin_pass( &self.descriptor, &render_resource_assignments, &mut |render_pass| { - for pipeline_handle in self.pipelines.iter() { - if let Some(compiled_pipelines_iter) = - pipeline_compiler.iter_compiled_pipelines(*pipeline_handle) - { - for compiled_pipeline_handle in compiled_pipelines_iter { - let compiled_pipeline_descriptor = - pipelines.get(compiled_pipeline_handle).unwrap(); - render_pass.set_pipeline(*compiled_pipeline_handle); + for (entity, draw) in >::query().iter_entities(&world) { + if !draw.is_visible || entities_waiting_for_assets.contains(&entity) { + continue; + } - // set global render resources - Self::set_render_resources( - render_pass, - compiled_pipeline_descriptor, - &render_resource_assignments, - ); - - // draw entities assigned to this pipeline - let assigned_render_resource_assignments = shader_pipeline_assignments - .assignments - .get(&compiled_pipeline_handle); - - 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 - || entities_waiting_for_assets.contains(entity) - { - continue; - } - - // set local render resources - if let Some(indices) = Self::set_render_resources( - render_pass, - compiled_pipeline_descriptor, - &renderable.render_resource_assignments, - ) { - render_pass.draw_indexed(indices, 0, 0..1); - } - } + for render_command in draw.render_commands.iter() { + match render_command { + RenderCommand::SetPipeline { pipeline } => { + // TODO: Filter pipelines + render_pass.set_pipeline(*pipeline); + } + RenderCommand::DrawIndexed { + base_vertex, + indices, + instances, + } => { + render_pass.draw_indexed( + indices.clone(), + *base_vertex, + instances.clone(), + ); + } + RenderCommand::SetVertexBuffer { + buffer, + offset, + slot, + } => { + render_pass.set_vertex_buffer(*slot, *buffer, *offset); + } + RenderCommand::SetIndexBuffer { buffer, offset } => { + render_pass.set_index_buffer(*buffer, *offset); + } + RenderCommand::SetBindGroup { + index, + bind_group_descriptor, + render_resource_set, + dynamic_uniform_indices, + } => { + render_pass.set_bind_group( + *index, + *bind_group_descriptor, + *render_resource_set, + dynamic_uniform_indices + .as_ref() + .map(|indices| indices.as_slice()), + ); } } } diff --git a/crates/bevy_render/src/render_graph/nodes/uniform_node.rs b/crates/bevy_render/src/render_graph/nodes/uniform_node.rs index cb4ab623e9..ac5744697c 100644 --- a/crates/bevy_render/src/render_graph/nodes/uniform_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/uniform_node.rs @@ -1,11 +1,13 @@ use crate::{ + draw::{Draw, RenderPipelines}, render_graph::{CommandQueue, Node, ResourceSlots, SystemNode}, render_resource::{ self, BufferInfo, BufferUsage, EntitiesWaitingForAssets, RenderResourceAssignment, - RenderResourceAssignments, RenderResourceAssignmentsId, RenderResourceId, RenderResourceHints, + RenderResourceAssignments, RenderResourceAssignmentsId, RenderResourceHints, + RenderResourceId, }, renderer::{RenderContext, RenderResourceContext, RenderResources}, - texture, Renderable, + texture, }; use bevy_asset::{Assets, Handle}; @@ -217,12 +219,14 @@ where None => { // TODO: RE-ADD support for BufferUsage::STORAGE type let mut usage = BufferUsage::UNIFORM; - - if let Some(render_resource_hints) = uniforms.get_render_resource_hints(i) { + + if let Some(render_resource_hints) = + uniforms.get_render_resource_hints(i) + { if render_resource_hints.contains(RenderResourceHints::BUFFER) { usage = BufferUsage::STORAGE } - } + } let resource = render_resources.create_buffer(BufferInfo { size, @@ -396,8 +400,8 @@ where .read_resource::() .read_resource::() // TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same - .with_query(<(Read, Read)>::query()) - .with_query(<(Read, Write)>::query()) + .with_query(<(Read, Read, Read)>::query()) + .with_query(<(Read, Read, Write)>::query()) .build( move |_, world, @@ -407,60 +411,50 @@ where uniform_buffer_arrays.reset_new_item_counts(); // update uniforms info - for (uniforms, renderable) in read_uniform_query.iter(world) { - if !renderable.is_visible { + for (uniforms, draw, _render_pipelines) in read_uniform_query.iter(world) { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - uniform_buffer_arrays.increment_uniform_counts(&uniforms); - } + uniform_buffer_arrays.increment_uniform_counts(&uniforms); } uniform_buffer_arrays .setup_buffer_arrays(render_resource_context, dynamic_uniforms); let staging_buffer_size = uniform_buffer_arrays.update_staging_buffer_offsets(); - for (entity, (uniforms, mut renderable)) in + for (entity, (uniforms, draw, mut render_pipelines)) in write_uniform_query.iter_entities_mut(world) { - if !renderable.is_visible { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - setup_uniform_texture_resources::( - entity, - &uniforms, - render_resource_context, - entities_waiting_for_assets, - &mut renderable.render_resource_assignments, - ) - } + setup_uniform_texture_resources::( + entity, + &uniforms, + render_resource_context, + entities_waiting_for_assets, + &mut render_pipelines.render_resource_assignments, + ) } if staging_buffer_size == 0 { let mut staging_buffer: [u8; 0] = []; - for (uniforms, mut renderable) in write_uniform_query.iter_mut(world) { - if !renderable.is_visible { + for (uniforms, draw, mut render_pipelines) in + write_uniform_query.iter_mut(world) + { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - uniform_buffer_arrays.setup_uniform_buffer_resources( - &uniforms, - dynamic_uniforms, - render_resource_context, - &mut renderable.render_resource_assignments, - &mut staging_buffer, - ); - } + uniform_buffer_arrays.setup_uniform_buffer_resources( + &uniforms, + dynamic_uniforms, + render_resource_context, + &mut render_pipelines.render_resource_assignments, + &mut staging_buffer, + ); } } else { let staging_buffer = render_resource_context.create_buffer_mapped( @@ -470,22 +464,20 @@ where ..Default::default() }, &mut |mut staging_buffer, _render_resources| { - for (uniforms, mut renderable) in write_uniform_query.iter_mut(world) { - if !renderable.is_visible { + for (uniforms, draw, mut render_pipelines) in + write_uniform_query.iter_mut(world) + { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - uniform_buffer_arrays.setup_uniform_buffer_resources( - &uniforms, - dynamic_uniforms, - render_resource_context, - &mut renderable.render_resource_assignments, - &mut staging_buffer, - ); - } + uniform_buffer_arrays.setup_uniform_buffer_resources( + &uniforms, + dynamic_uniforms, + render_resource_context, + &mut render_pipelines.render_resource_assignments, + &mut staging_buffer, + ); } }, ); @@ -552,8 +544,8 @@ where .read_resource::() .read_resource::() // TODO: this write on RenderResourceAssignments will prevent this system from running in parallel with other systems that do the same - .with_query(<(Read>, Read)>::query()) - .with_query(<(Read>, Write)>::query()) + .with_query(<(Read>, Read, Read)>::query()) + .with_query(<(Read>, Read, Write)>::query()) .build( move |_, world, @@ -563,20 +555,18 @@ where uniform_buffer_arrays.reset_new_item_counts(); // update uniform handles info - for (entity, (handle, renderable)) in read_handle_query.iter_entities(world) { - if !renderable.is_visible { + for (entity, (handle, draw, _render_pipelines)) in + read_handle_query.iter_entities(world) + { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); + if let Some(uniforms) = assets.get(&handle) { + // TODO: only increment count if we haven't seen this uniform handle before + uniform_buffer_arrays.increment_uniform_counts(&uniforms); } else { - if let Some(uniforms) = assets.get(&handle) { - // TODO: only increment count if we haven't seen this uniform handle before - uniform_buffer_arrays.increment_uniform_counts(&uniforms); - } else { - entities_waiting_for_assets.add(entity) - } + entities_waiting_for_assets.add(entity) } } @@ -584,46 +574,40 @@ where .setup_buffer_arrays(render_resource_context, dynamic_uniforms); let staging_buffer_size = uniform_buffer_arrays.update_staging_buffer_offsets(); - for (entity, (handle, mut renderable)) in + for (entity, (handle, draw, mut render_pipelines)) in write_handle_query.iter_entities_mut(world) { - if !renderable.is_visible { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - if let Some(uniforms) = assets.get(&handle) { - setup_uniform_texture_resources::( - entity, - &uniforms, - render_resource_context, - entities_waiting_for_assets, - &mut renderable.render_resource_assignments, - ) - } + if let Some(uniforms) = assets.get(&handle) { + setup_uniform_texture_resources::( + entity, + &uniforms, + render_resource_context, + entities_waiting_for_assets, + &mut render_pipelines.render_resource_assignments, + ) } } if staging_buffer_size == 0 { let mut staging_buffer: [u8; 0] = []; - for (handle, mut renderable) in write_handle_query.iter_mut(world) { - if !renderable.is_visible { + for (handle, draw, mut render_pipelines) in + write_handle_query.iter_mut(world) + { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - if let Some(uniforms) = assets.get(&handle) { - // TODO: only setup buffer if we haven't seen this handle before - uniform_buffer_arrays.setup_uniform_buffer_resources( - &uniforms, - dynamic_uniforms, - render_resource_context, - &mut renderable.render_resource_assignments, - &mut staging_buffer, - ); - } + if let Some(uniforms) = assets.get(&handle) { + // TODO: only setup buffer if we haven't seen this handle before + uniform_buffer_arrays.setup_uniform_buffer_resources( + &uniforms, + dynamic_uniforms, + render_resource_context, + &mut render_pipelines.render_resource_assignments, + &mut staging_buffer, + ); } } } else { @@ -634,23 +618,21 @@ where ..Default::default() }, &mut |mut staging_buffer, _render_resources| { - for (handle, mut renderable) in write_handle_query.iter_mut(world) { - if !renderable.is_visible { + for (handle, draw, mut render_pipelines) in + write_handle_query.iter_mut(world) + { + if !draw.is_visible { return; } - if renderable.is_instanced { - panic!("instancing not currently supported"); - } else { - if let Some(uniforms) = assets.get(&handle) { - // TODO: only setup buffer if we haven't seen this handle before - uniform_buffer_arrays.setup_uniform_buffer_resources( - &uniforms, - dynamic_uniforms, - render_resource_context, - &mut renderable.render_resource_assignments, - &mut staging_buffer, - ); - } + if let Some(uniforms) = assets.get(&handle) { + // TODO: only setup buffer if we haven't seen this handle before + uniform_buffer_arrays.setup_uniform_buffer_resources( + &uniforms, + dynamic_uniforms, + render_resource_context, + &mut render_pipelines.render_resource_assignments, + &mut staging_buffer, + ); } } }, diff --git a/crates/bevy_render/src/render_resource/entity_render_resource_assignments.rs b/crates/bevy_render/src/render_resource/entity_render_resource_assignments.rs deleted file mode 100644 index 151e20d1e4..0000000000 --- a/crates/bevy_render/src/render_resource/entity_render_resource_assignments.rs +++ /dev/null @@ -1,31 +0,0 @@ -use super::RenderResourceAssignmentsId; -use crate::Renderable; -use legion::prelude::*; -use std::collections::HashMap; - -#[derive(Default)] -pub struct EntityRenderResourceAssignments { - entity_assignments: HashMap, -} - -impl EntityRenderResourceAssignments { - pub fn set(&mut self, id: RenderResourceAssignmentsId, entity: Entity) { - self.entity_assignments.insert(id, entity); - } - - pub fn get(&self, id: RenderResourceAssignmentsId) -> Option<&Entity> { - self.entity_assignments.get(&id) - } -} - -// TODO: make sure this runs right before rendering -pub fn entity_render_resource_assignments_system() -> Box { - SystemBuilder::new("entity_render_resource_assignments") - .write_resource::() - .with_query(>::query().filter(changed::())) - .build(|_, world, entity_assignments, query| { - for (entity, renderable) in query.iter_entities_mut(world) { - entity_assignments.set(renderable.render_resource_assignments.id, entity); - } - }) -} diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index 0882bf62ae..9e5c9d254f 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -1,13 +1,13 @@ mod buffer; mod entities_waiting_for_assets; -mod entity_render_resource_assignments; mod render_resource; mod render_resource_assignments; mod resource_info; +mod systems; pub use buffer::*; pub use entities_waiting_for_assets::*; -pub use entity_render_resource_assignments::*; pub use render_resource::*; pub use render_resource_assignments::*; pub use resource_info::*; +pub use systems::*; \ No newline at end of file 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 c9c53e6585..47c40333f3 100644 --- a/crates/bevy_render/src/render_resource/render_resource_assignments.rs +++ b/crates/bevy_render/src/render_resource/render_resource_assignments.rs @@ -4,6 +4,7 @@ use std::{ collections::{hash_map::DefaultHasher, HashMap, HashSet}, hash::{Hash, Hasher}, ops::Range, + sync::Arc, }; use uuid::Uuid; @@ -28,10 +29,25 @@ impl RenderResourceAssignment { } } +#[derive(Eq, PartialEq, Debug)] +pub struct IndexedRenderResourceAssignment { + pub index: u32, + pub assignment: RenderResourceAssignment, +} + +// TODO: consider renaming this to BindGroup for parity with renderer terminology #[derive(Eq, PartialEq, Debug)] pub struct RenderResourceSet { pub id: RenderResourceSetId, - pub dynamic_uniform_indices: Option>, + pub indexed_assignments: Vec, + pub dynamic_uniform_indices: Option>>, +} + +#[derive(Eq, PartialEq, Debug)] +pub enum RenderResourceSetStatus { + Changed(RenderResourceSetId), + Unchanged(RenderResourceSetId), + NoMatch, } // PERF: if the assignments are scoped to a specific pipeline layout, then names could be replaced with indices here for a perf boost @@ -40,8 +56,9 @@ pub struct RenderResourceAssignments { pub id: RenderResourceAssignmentsId, render_resources: HashMap, vertex_buffers: HashMap)>, - bind_group_resource_sets: HashMap, - dirty_bind_groups: HashSet, + render_resource_sets: HashMap, + bind_group_render_resource_sets: HashMap, + dirty_render_resource_sets: HashSet, pub pipeline_specialization: PipelineSpecialization, } @@ -58,9 +75,8 @@ impl RenderResourceAssignments { fn try_set_dirty(&mut self, name: &str, assignment: &RenderResourceAssignment) { if let Some(current_assignment) = self.render_resources.get(name) { if current_assignment != assignment { - // TODO: this is pretty crude. can we do better? - for bind_group_id in self.bind_group_resource_sets.keys() { - self.dirty_bind_groups.insert(*bind_group_id); + for id in self.render_resource_sets.keys() { + self.dirty_render_resource_sets.insert(*id); } } } @@ -83,36 +99,54 @@ impl RenderResourceAssignments { .insert(name.to_string(), (vertices_resource, indices_resource)); } - pub fn update_render_resource_set_id( + fn create_render_resource_set( &mut self, bind_group_descriptor: &BindGroupDescriptor, - ) -> Option { - if !self - .bind_group_resource_sets - .contains_key(&bind_group_descriptor.id) - || self.dirty_bind_groups.contains(&bind_group_descriptor.id) - { - let resource_set = self.generate_render_resource_set(bind_group_descriptor); - if let Some(resource_set) = resource_set { - let id = resource_set.id; - self.bind_group_resource_sets - .insert(bind_group_descriptor.id, resource_set); - Some(id) - } else { - None - } + ) -> RenderResourceSetStatus { + let resource_set = self.generate_render_resource_set(bind_group_descriptor); + if let Some(resource_set) = resource_set { + let id = resource_set.id; + self.render_resource_sets.insert(id, resource_set); + self.bind_group_render_resource_sets + .insert(bind_group_descriptor.id, id); + RenderResourceSetStatus::Changed(id) } else { - self.bind_group_resource_sets - .get(&bind_group_descriptor.id) - .map(|set| set.id) + RenderResourceSetStatus::NoMatch } } - pub fn get_render_resource_set( + pub fn update_bind_group( + &mut self, + bind_group_descriptor: &BindGroupDescriptor, + ) -> RenderResourceSetStatus { + if let Some(id) = self + .bind_group_render_resource_sets + .get(&bind_group_descriptor.id) + { + if self.dirty_render_resource_sets.contains(id) { + self.dirty_render_resource_sets.remove(id); + self.create_render_resource_set(bind_group_descriptor) + } else { + RenderResourceSetStatus::Unchanged(*id) + } + } else { + self.create_render_resource_set(bind_group_descriptor) + } + } + + pub fn get_render_resource_set(&self, id: RenderResourceSetId) -> Option<&RenderResourceSet> { + self.render_resource_sets.get(&id) + } + + pub fn get_bind_group_render_resource_set( &self, - bind_group_descriptor_id: BindGroupDescriptorId, + id: BindGroupDescriptorId, ) -> Option<&RenderResourceSet> { - self.bind_group_resource_sets.get(&bind_group_descriptor_id) + self.bind_group_render_resource_sets + .get(&id) + .and_then(|render_resource_set_id| { + self.get_render_resource_set(*render_resource_set_id) + }) } fn generate_render_resource_set( @@ -121,8 +155,13 @@ impl RenderResourceAssignments { ) -> Option { let mut hasher = DefaultHasher::new(); let mut indices = Vec::new(); + let mut indexed_assignments = Vec::new(); for binding_descriptor in bind_group_descriptor.bindings.iter() { if let Some(assignment) = self.get(&binding_descriptor.name) { + indexed_assignments.push(IndexedRenderResourceAssignment { + assignment: assignment.clone(), + index: binding_descriptor.index, + }); let resource = assignment.get_resource(); resource.hash(&mut hasher); if let RenderResourceAssignment::Buffer { @@ -139,10 +178,11 @@ impl RenderResourceAssignments { Some(RenderResourceSet { id: RenderResourceSetId(hasher.finish()), + indexed_assignments, dynamic_uniform_indices: if indices.is_empty() { None } else { - Some(indices) + Some(Arc::new(indices)) }, }) } @@ -215,22 +255,34 @@ mod tests { equal_assignments.set("a", resource1.clone()); equal_assignments.set("b", resource2.clone()); - let set_id = assignments.update_render_resource_set_id(&bind_group_descriptor); - assert_ne!(set_id, None); + let status = assignments.update_bind_group(&bind_group_descriptor); + let id = if let RenderResourceSetStatus::Changed(id) = status { + id + } else { + panic!("expected a changed render resource set"); + }; - let different_set_id = - different_assignments.update_render_resource_set_id(&bind_group_descriptor); - assert_ne!(different_set_id, None); - assert_ne!(different_set_id, set_id); + let different_set_status = different_assignments.update_bind_group(&bind_group_descriptor); + if let RenderResourceSetStatus::Changed(different_set_id) = different_set_status { + assert_ne!( + id, different_set_id, + "different set shouldn't have the same id" + ); + different_set_id + } else { + panic!("expected a changed render resource set"); + }; - let equal_set_id = equal_assignments.update_render_resource_set_id(&bind_group_descriptor); - assert_ne!(equal_set_id, None); - assert_eq!(equal_set_id, set_id); + let equal_set_status = equal_assignments.update_bind_group(&bind_group_descriptor); + if let RenderResourceSetStatus::Changed(equal_set_id) = equal_set_status { + assert_eq!(id, equal_set_id, "equal set should have the same id"); + } else { + panic!("expected a changed render resource set"); + }; let mut unmatched_assignments = RenderResourceAssignments::default(); unmatched_assignments.set("a", resource1.clone()); - let unmatched_set_id = - unmatched_assignments.update_render_resource_set_id(&bind_group_descriptor); - assert_eq!(unmatched_set_id, None); + let unmatched_set_status = unmatched_assignments.update_bind_group(&bind_group_descriptor); + assert_eq!(unmatched_set_status, RenderResourceSetStatus::NoMatch); } } diff --git a/crates/bevy_render/src/render_resource/systems.rs b/crates/bevy_render/src/render_resource/systems.rs new file mode 100644 index 0000000000..0874d0be70 --- /dev/null +++ b/crates/bevy_render/src/render_resource/systems.rs @@ -0,0 +1,68 @@ +use bevy_asset::Assets; +use crate::{ + draw::RenderPipelines, + pipeline::{PipelineCompiler, PipelineDescriptor}, + render_resource::{RenderResourceAssignments, RenderResourceSetStatus}, + renderer::{RenderResourceContext, RenderResources}, +}; +use legion::prelude::*; + +fn update_bind_groups( + pipeline: &PipelineDescriptor, + render_resource_assignments: &mut RenderResourceAssignments, + render_resource_context: &dyn RenderResourceContext, +) { + let layout = pipeline.get_layout().unwrap(); + for bind_group in layout.bind_groups.iter() { + match render_resource_assignments.update_bind_group(bind_group) { + RenderResourceSetStatus::Changed(id) => { + let render_resource_set = render_resource_assignments + .get_render_resource_set(id) + .expect("RenderResourceSet was just changed, so it should exist"); + render_resource_context.create_bind_group(bind_group.id, render_resource_set); + }, + // TODO: Don't re-create bind groups if they havent changed. this will require cleanup of orphan bind groups and + // removal of global context.clear_bind_groups() + // PERF: see above + RenderResourceSetStatus::Unchanged(id) => { + let render_resource_set = render_resource_assignments + .get_render_resource_set(id) + .expect("RenderResourceSet was just changed, so it should exist"); + render_resource_context.create_bind_group(bind_group.id, render_resource_set); + }, + RenderResourceSetStatus::NoMatch => { + // ignore unchanged / unmatched render resource sets + } + } + } +} + +pub fn render_resource_sets_system( + world: &mut SubWorld, + pipelines: Res>, + pipeline_compiler: Res, + render_resources: Res, + mut render_resource_assignments: ResMut, + query: &mut Query>, +) { + let render_resource_context = &*render_resources.context; + for compiled_pipeline_handle in pipeline_compiler.iter_all_compiled_pipelines() { + let pipeline = pipelines.get(compiled_pipeline_handle).unwrap(); + update_bind_groups( + pipeline, + &mut render_resource_assignments, + render_resource_context, + ) + } + for mut render_pipelines in query.iter_mut(world) { + let render_pipelines = render_pipelines.as_mut(); + for pipeline in render_pipelines.compiled_pipelines.iter() { + let pipeline = pipelines.get(pipeline).unwrap(); + update_bind_groups( + pipeline, + &mut render_pipelines.render_resource_assignments, + render_resource_context, + ) + } + } +} diff --git a/crates/bevy_render/src/renderable.rs b/crates/bevy_render/src/renderable.rs deleted file mode 100644 index a741656ce8..0000000000 --- a/crates/bevy_render/src/renderable.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::{pipeline::PipelineDescriptor, render_resource::RenderResourceAssignments}; -use bevy_asset::Handle; -use bevy_property::Properties; - -#[derive(Properties)] -pub struct Renderable { - pub is_visible: bool, - pub is_instanced: bool, - pub pipelines: Vec>, - #[property(ignore)] - pub render_resource_assignments: RenderResourceAssignments, -} - -impl Renderable { - pub fn instanced() -> Self { - Renderable { - is_instanced: true, - ..Default::default() - } - } -} - -impl Default for Renderable { - fn default() -> Self { - Renderable { - is_visible: true, - pipelines: vec![Handle::default()], - render_resource_assignments: RenderResourceAssignments::default(), - is_instanced: false, - } - } -} diff --git a/crates/bevy_render/src/renderer/headless_render_resource_context.rs b/crates/bevy_render/src/renderer/headless_render_resource_context.rs index f84cb4c6d6..299283bb1f 100644 --- a/crates/bevy_render/src/renderer/headless_render_resource_context.rs +++ b/crates/bevy_render/src/renderer/headless_render_resource_context.rs @@ -1,9 +1,7 @@ use super::RenderResourceContext; use crate::{ - pipeline::{BindGroupDescriptor, PipelineDescriptor}, - render_resource::{ - BufferInfo, RenderResourceId, RenderResourceAssignments, RenderResourceSetId, ResourceInfo, - }, + pipeline::{BindGroupDescriptorId, PipelineDescriptor}, + render_resource::{BufferInfo, RenderResourceId, RenderResourceSet, ResourceInfo}, shader::Shader, texture::{SamplerDescriptor, TextureDescriptor}, }; @@ -113,16 +111,9 @@ impl RenderResourceContext for HeadlessRenderResourceContext { } fn create_bind_group( &self, - bind_group_descriptor: &BindGroupDescriptor, - render_resource_assignments: &RenderResourceAssignments, - ) -> Option { - if let Some(resource_set) = - render_resource_assignments.get_render_resource_set(bind_group_descriptor.id) - { - Some(resource_set.id) - } else { - None - } + _bind_group_descriptor_id: BindGroupDescriptorId, + _render_resource_set: &RenderResourceSet, + ) { } fn create_shader_module_from_source(&self, _shader_handle: Handle, _shader: &Shader) {} fn remove_asset_resource_untyped(&self, handle: HandleUntyped, index: usize) { diff --git a/crates/bevy_render/src/renderer/render_resource_context.rs b/crates/bevy_render/src/renderer/render_resource_context.rs index 35e30e2456..a939952aa1 100644 --- a/crates/bevy_render/src/renderer/render_resource_context.rs +++ b/crates/bevy_render/src/renderer/render_resource_context.rs @@ -1,8 +1,6 @@ use crate::{ - pipeline::{BindGroupDescriptor, PipelineDescriptor}, - render_resource::{ - BufferInfo, RenderResourceId, RenderResourceAssignments, RenderResourceSetId, ResourceInfo, - }, + pipeline::{BindGroupDescriptorId, PipelineDescriptor}, + render_resource::{BufferInfo, RenderResourceId, RenderResourceSet, ResourceInfo}, shader::Shader, texture::{SamplerDescriptor, TextureDescriptor}, }; @@ -70,29 +68,15 @@ pub trait RenderResourceContext: Downcast + Send + Sync + 'static { ); fn create_bind_group( &self, - bind_group_descriptor: &BindGroupDescriptor, - render_resource_assignments: &RenderResourceAssignments, - ) -> Option; - fn setup_bind_groups( - &self, - pipeline_descriptor: &PipelineDescriptor, - render_resource_assignments: &RenderResourceAssignments, - ) { - let pipeline_layout = pipeline_descriptor.get_layout().unwrap(); - for bind_group in pipeline_layout.bind_groups.iter() { - self.create_bind_group(bind_group, render_resource_assignments); - } - } + bind_group_descriptor_id: BindGroupDescriptorId, + render_resource_set: &RenderResourceSet, + ); fn clear_bind_groups(&self); } impl dyn RenderResourceContext { - pub fn set_asset_resource( - &self, - handle: Handle, - resource: RenderResourceId, - index: usize, - ) where + pub fn set_asset_resource(&self, handle: Handle, resource: RenderResourceId, index: usize) + where T: 'static, { self.set_asset_resource_untyped(handle.into(), resource, index); diff --git a/crates/bevy_render/src/shader/shader_defs.rs b/crates/bevy_render/src/shader/shader_defs.rs index e1fc1a9a74..f62e136586 100644 --- a/crates/bevy_render/src/shader/shader_defs.rs +++ b/crates/bevy_render/src/shader/shader_defs.rs @@ -1,7 +1,6 @@ - +use crate::{texture::Texture, RenderPipelines}; use bevy_asset::{Assets, Handle}; -use crate::{Renderable, texture::Texture}; -use legion::prelude::{Res, Com, ComMut}; +use legion::prelude::{Com, ComMut, Res}; pub use bevy_derive::ShaderDefs; @@ -15,7 +14,6 @@ pub trait ShaderDefs { fn iter_shader_defs(&self) -> ShaderDefIterator; } - pub struct ShaderDefIterator<'a> { shader_defs: &'a dyn ShaderDefs, index: usize, @@ -35,13 +33,11 @@ impl<'a> Iterator for ShaderDefIterator<'a> { loop { if self.index == self.shader_defs.shader_defs_len() { return None; - } - let shader_def = self - .shader_defs - .get_shader_def(self.index); + } + let shader_def = self.shader_defs.get_shader_def(self.index); self.index += 1; if shader_def.is_some() { - return shader_def; + return shader_def; } } } @@ -59,12 +55,12 @@ impl ShaderDef for Option> { } } -pub fn shader_def_system(shader_defs: Com, mut renderable: ComMut) +pub fn shader_def_system(shader_defs: Com, mut render_pipelines: ComMut) where T: ShaderDefs + Send + Sync + 'static, { for shader_def in shader_defs.iter_shader_defs() { - renderable + render_pipelines .render_resource_assignments .pipeline_specialization .shader_specialization @@ -76,17 +72,17 @@ where pub fn asset_shader_def_system( assets: Res>, asset_handle: Com>, - mut renderable: ComMut, + mut render_pipelines: ComMut, ) where T: ShaderDefs + Send + Sync + 'static, { let shader_defs = assets.get(&asset_handle).unwrap(); for shader_def in shader_defs.iter_shader_defs() { - renderable + render_pipelines .render_resource_assignments .pipeline_specialization .shader_specialization .shader_defs .insert(shader_def.to_string()); } -} \ No newline at end of file +} diff --git a/crates/bevy_sprite/src/entity.rs b/crates/bevy_sprite/src/entity.rs index f4cbec75ed..f3311312c9 100644 --- a/crates/bevy_sprite/src/entity.rs +++ b/crates/bevy_sprite/src/entity.rs @@ -4,7 +4,7 @@ use crate::{ }; use bevy_asset::Handle; use bevy_app::EntityArchetype; -use bevy_render::{mesh::Mesh, Renderable}; +use bevy_render::{mesh::Mesh, draw::{Draw, RenderPipelines}}; #[derive(EntityArchetype)] pub struct SpriteEntity { @@ -12,7 +12,8 @@ pub struct SpriteEntity { pub quad: Quad, pub mesh: Handle, // TODO: maybe abstract this out pub material: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, } impl Default for SpriteEntity { @@ -22,7 +23,8 @@ impl Default for SpriteEntity { quad: Default::default(), mesh: QUAD_HANDLE, material: Default::default(), - renderable: Renderable { + draw: Default::default(), + render_pipelines: RenderPipelines { pipelines: vec![SPRITE_PIPELINE_HANDLE], ..Default::default() }, @@ -34,7 +36,8 @@ impl Default for SpriteEntity { pub struct SpriteSheetEntity { pub sprite: TextureAtlasSprite, pub texture_atlas: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, pub mesh: Handle, // TODO: maybe abstract this out // pub transform: Transform, // pub translation: Translation, @@ -47,7 +50,8 @@ impl Default for SpriteSheetEntity { Self { sprite: Default::default(), texture_atlas: Default::default(), - renderable: Renderable { + draw: Default::default(), + render_pipelines: RenderPipelines { pipelines: vec![SPRITE_SHEET_PIPELINE_HANDLE], ..Default::default() }, diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 910ea4354c..e70515d562 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -4,7 +4,7 @@ use bevy_render::{ base_render_graph, pipeline::{state_descriptors::*, PipelineDescriptor}, render_graph::{ - nodes::{AssetUniformNode, PassNode, UniformNode}, + nodes::{AssetUniformNode, UniformNode}, RenderGraph, }, shader::{Shader, ShaderStage, ShaderStages}, @@ -149,11 +149,6 @@ impl SpriteRenderGraphBuilder for RenderGraph { SPRITE_SHEET_PIPELINE_HANDLE, build_sprite_sheet_pipeline(&mut shaders), ); - let main_pass: &mut PassNode = self - .get_node_mut(base_render_graph::node::MAIN_PASS) - .unwrap(); - main_pass.add_pipeline(SPRITE_PIPELINE_HANDLE); - main_pass.add_pipeline(SPRITE_SHEET_PIPELINE_HANDLE); self } } diff --git a/crates/bevy_ui/src/entity.rs b/crates/bevy_ui/src/entity.rs index 3de70d32ff..67aa57f802 100644 --- a/crates/bevy_ui/src/entity.rs +++ b/crates/bevy_ui/src/entity.rs @@ -2,7 +2,7 @@ use super::Node; use crate::{render::UI_PIPELINE_HANDLE, widget::Label}; use bevy_asset::Handle; use bevy_derive::EntityArchetype; -use bevy_render::{mesh::Mesh, Renderable}; +use bevy_render::{draw::{Draw, RenderPipelines}, mesh::Mesh}; use bevy_sprite::{ColorMaterial, Quad, QUAD_HANDLE}; #[derive(EntityArchetype)] @@ -11,7 +11,8 @@ pub struct UiEntity { pub quad: Quad, pub mesh: Handle, // TODO: maybe abstract this out pub material: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, } impl Default for UiEntity { @@ -21,7 +22,8 @@ impl Default for UiEntity { quad: Default::default(), mesh: QUAD_HANDLE, material: Default::default(), - renderable: Renderable { + draw: Default::default(), + render_pipelines: RenderPipelines { pipelines: vec![UI_PIPELINE_HANDLE], ..Default::default() }, @@ -35,7 +37,8 @@ pub struct LabelEntity { pub quad: Quad, pub mesh: Handle, // TODO: maybe abstract this out pub material: Handle, - pub renderable: Renderable, + pub draw: Draw, + pub render_pipelines: RenderPipelines, pub label: Label, } @@ -47,7 +50,8 @@ impl Default for LabelEntity { mesh: QUAD_HANDLE, // NOTE: labels each get their own material. material: Handle::new(), // TODO: maybe abstract this out - renderable: Renderable { + draw: Default::default(), + render_pipelines: RenderPipelines { pipelines: vec![UI_PIPELINE_HANDLE], ..Default::default() }, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 244853b9f6..70f2479abd 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,10 +2,7 @@ use bevy_asset::{Assets, Handle}; use bevy_render::{ base_render_graph, pipeline::{state_descriptors::*, PipelineDescriptor}, - render_graph::{ - nodes::{CameraNode, PassNode}, - RenderGraph, - }, + render_graph::{nodes::CameraNode, RenderGraph}, shader::{Shader, ShaderStage, ShaderStages}, texture::TextureFormat, }; @@ -79,10 +76,6 @@ impl UiRenderGraphBuilder for RenderGraph { let mut pipelines = resources.get_mut::>().unwrap(); let mut shaders = resources.get_mut::>().unwrap(); pipelines.set(UI_PIPELINE_HANDLE, build_ui_pipeline(&mut shaders)); - let main_pass: &mut PassNode = self - .get_node_mut(base_render_graph::node::MAIN_PASS) - .unwrap(); - main_pass.add_pipeline(UI_PIPELINE_HANDLE); self } } diff --git a/crates/bevy_wgpu/src/lib.rs b/crates/bevy_wgpu/src/lib.rs index 150ab5d8fa..aa066b1f6d 100644 --- a/crates/bevy_wgpu/src/lib.rs +++ b/crates/bevy_wgpu/src/lib.rs @@ -9,9 +9,8 @@ pub use wgpu_render_pass::*; pub use wgpu_renderer::*; pub use wgpu_resources::*; -use bevy_app::{AppBuilder, AppPlugin, Events}; +use bevy_app::{AppBuilder, AppPlugin}; use bevy_render::renderer::RenderResources; -use bevy_window::{WindowCreated, WindowResized}; use legion::prelude::*; use renderer::WgpuRenderResourceContext; @@ -26,14 +25,7 @@ impl AppPlugin for WgpuPlugin { } pub fn wgpu_render_system(resources: &mut Resources) -> impl FnMut(&mut World, &mut Resources) { - let mut wgpu_renderer = { - let window_resized_event = resources.get::>().unwrap(); - let window_created_event = resources.get::>().unwrap(); - pollster::block_on(WgpuRenderer::new( - window_resized_event.get_reader(), - window_created_event.get_reader(), - )) - }; + let mut wgpu_renderer = pollster::block_on(WgpuRenderer::new()); resources.insert(RenderResources::new(WgpuRenderResourceContext::new( wgpu_renderer.device.clone(), ))); diff --git a/crates/bevy_wgpu/src/renderer/mod.rs b/crates/bevy_wgpu/src/renderer/mod.rs index 7c65a3eba3..3ab7d55b2f 100644 --- a/crates/bevy_wgpu/src/renderer/mod.rs +++ b/crates/bevy_wgpu/src/renderer/mod.rs @@ -1,9 +1,7 @@ -mod systems; mod wgpu_render_context; mod wgpu_render_graph_executor; mod wgpu_render_resource_context; -pub use systems::*; pub use wgpu_render_context::*; pub use wgpu_render_graph_executor::*; pub use wgpu_render_resource_context::*; diff --git a/crates/bevy_wgpu/src/renderer/systems.rs b/crates/bevy_wgpu/src/renderer/systems.rs deleted file mode 100644 index 3ff9e484be..0000000000 --- a/crates/bevy_wgpu/src/renderer/systems.rs +++ /dev/null @@ -1,92 +0,0 @@ -use bevy_asset::Assets; -use bevy_render::{ - pipeline::{PipelineAssignments, PipelineCompiler, PipelineDescriptor}, - render_resource::EntityRenderResourceAssignments, - Renderable, -}; -use legion::prelude::*; - -// TODO: replace with system_fn once configurable "archetype access" is sorted out -// pub fn render_resource_sets_system( -// world: &mut SubWorld, -// pipelines: Res>, -// pipeline_compiler: Res, -// pipeline_assignments: Res, -// entity_render_resource_assignments: Res, -// query: &mut Query>, // gives SubWorld write access to Renderable -// ) { -// // PERF: consider doing a par-iter over all renderable components so this can be parallelized -// for compiled_pipeline_handle in pipeline_compiler.iter_all_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 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); -// } -// } -// } -// } -// } - -pub fn render_resource_sets_system() -> Box { - SystemBuilder::new("update_render_resource_sets") - .read_resource::>() - .read_resource::() - .read_resource::() - .read_resource::() - .write_component::() - .build( - |_, - world, - ( - pipelines, - pipeline_compiler, - pipeline_assignments, - entity_render_resource_assignments, - ), - _| { - // PERF: consider doing a par-iter over all renderable components so this can be parallelized - for compiled_pipeline_handle in pipeline_compiler.iter_all_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 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); - } - } - } - } - }, - ) -} diff --git a/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs b/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs index 5c48cceee3..ba4bed9cad 100644 --- a/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs +++ b/crates/bevy_wgpu/src/renderer/wgpu_render_resource_context.rs @@ -5,10 +5,9 @@ use crate::{ use bevy_asset::{Assets, Handle, HandleUntyped}; use bevy_render::{ - pipeline::{BindGroupDescriptor, PipelineDescriptor}, + pipeline::{BindGroupDescriptor, BindGroupDescriptorId, PipelineDescriptor}, render_resource::{ - BufferInfo, RenderResourceId, RenderResourceAssignment, RenderResourceAssignments, - RenderResourceSetId, ResourceInfo, + BufferInfo, RenderResourceAssignment, RenderResourceId, RenderResourceSet, ResourceInfo, }, renderer::RenderResourceContext, shader::Shader, @@ -207,7 +206,11 @@ impl RenderResourceContext for WgpuRenderResourceContext { resource } - fn create_buffer_with_data(&self, mut buffer_info: BufferInfo, data: &[u8]) -> RenderResourceId { + fn create_buffer_with_data( + &self, + mut buffer_info: BufferInfo, + data: &[u8], + ) -> RenderResourceId { // TODO: consider moving this below "create" for efficiency let mut resource_info = self.resources.resource_info.write().unwrap(); let mut buffers = self.resources.buffers.write().unwrap(); @@ -450,87 +453,69 @@ impl RenderResourceContext for WgpuRenderResourceContext { fn create_bind_group( &self, - bind_group_descriptor: &BindGroupDescriptor, - render_resource_assignments: &RenderResourceAssignments, - ) -> Option { - if let Some(render_resource_set) = - render_resource_assignments.get_render_resource_set(bind_group_descriptor.id) + bind_group_descriptor_id: BindGroupDescriptorId, + render_resource_set: &RenderResourceSet, + ) { + if !self + .resources + .has_bind_group(bind_group_descriptor_id, render_resource_set.id) { - if !self - .resources - .has_bind_group(bind_group_descriptor.id, render_resource_set.id) - { - log::trace!( - "start creating bind group for RenderResourceSet {:?}", - render_resource_set.id - ); - let texture_views = self.resources.texture_views.read().unwrap(); - let samplers = self.resources.samplers.read().unwrap(); - let buffers = self.resources.buffers.read().unwrap(); - let bind_group_layouts = self.resources.bind_group_layouts.read().unwrap(); - let mut bind_groups = self.resources.bind_groups.write().unwrap(); + log::trace!( + "start creating bind group for RenderResourceSet {:?}", + render_resource_set.id + ); + let texture_views = self.resources.texture_views.read().unwrap(); + let samplers = self.resources.samplers.read().unwrap(); + let buffers = self.resources.buffers.read().unwrap(); + let bind_group_layouts = self.resources.bind_group_layouts.read().unwrap(); + let mut bind_groups = self.resources.bind_groups.write().unwrap(); - let bindings = bind_group_descriptor - .bindings - .iter() - .map(|binding| { - if let Some(assignment) = render_resource_assignments.get(&binding.name) { - log::trace!( - "found binding {} ({}) assignment: {:?}", - binding.index, - binding.name, - assignment, - ); - let wgpu_resource = match assignment { - RenderResourceAssignment::Texture(resource) => { - let texture = texture_views.get(&resource).unwrap(); - wgpu::BindingResource::TextureView(texture) - } - RenderResourceAssignment::Sampler(resource) => { - let sampler = samplers.get(&resource).unwrap(); - wgpu::BindingResource::Sampler(sampler) - } - RenderResourceAssignment::Buffer { resource, range , .. } => { - let buffer = buffers.get(&resource).unwrap(); - wgpu::BindingResource::Buffer(buffer.slice(range.clone())) - } - }; - wgpu::Binding { - binding: binding.index, - resource: wgpu_resource, - } - } else { - panic!( - "No resource assigned to uniform \"{}\" for RenderResourceAssignments {:?}", - binding.name, - render_resource_assignments.id - ); + let bindings = render_resource_set + .indexed_assignments + .iter() + .map(|indexed_assignment| { + let wgpu_resource = match &indexed_assignment.assignment { + RenderResourceAssignment::Texture(resource) => { + let texture = texture_views.get(&resource).unwrap(); + wgpu::BindingResource::TextureView(texture) } - }) - .collect::>(); + RenderResourceAssignment::Sampler(resource) => { + let sampler = samplers.get(&resource).unwrap(); + wgpu::BindingResource::Sampler(sampler) + } + RenderResourceAssignment::Buffer { + resource, range, .. + } => { + let buffer = buffers.get(&resource).unwrap(); + wgpu::BindingResource::Buffer(buffer.slice(range.clone())) + } + }; + wgpu::Binding { + binding: indexed_assignment.index, + resource: wgpu_resource, + } + }) + .collect::>(); - let bind_group_layout = bind_group_layouts.get(&bind_group_descriptor.id).unwrap(); - let wgpu_bind_group_descriptor = wgpu::BindGroupDescriptor { - label: None, - layout: bind_group_layout, - bindings: bindings.as_slice(), - }; - let wgpu_bind_group = self.device.create_bind_group(&wgpu_bind_group_descriptor); + let bind_group_layout = bind_group_layouts.get(&bind_group_descriptor_id).unwrap(); + let wgpu_bind_group_descriptor = wgpu::BindGroupDescriptor { + label: None, + layout: bind_group_layout, + bindings: bindings.as_slice(), + }; + let wgpu_bind_group = self.device.create_bind_group(&wgpu_bind_group_descriptor); - let bind_group_info = bind_groups - .entry(bind_group_descriptor.id) - .or_insert_with(|| WgpuBindGroupInfo::default()); - bind_group_info - .bind_groups - .insert(render_resource_set.id, wgpu_bind_group); - log::trace!( - "created bind group for RenderResourceSet {:?}", - render_resource_set.id - ); - return Some(render_resource_set.id); - } + let bind_group_info = bind_groups + .entry(bind_group_descriptor_id) + .or_insert_with(|| WgpuBindGroupInfo::default()); + bind_group_info + .bind_groups + .insert(render_resource_set.id, wgpu_bind_group); + log::trace!( + "created bind group for RenderResourceSet {:?}", + render_resource_set.id + ); } - None } fn clear_bind_groups(&self) { diff --git a/crates/bevy_wgpu/src/wgpu_render_pass.rs b/crates/bevy_wgpu/src/wgpu_render_pass.rs index 0a5387c326..00fb161a52 100644 --- a/crates/bevy_wgpu/src/wgpu_render_pass.rs +++ b/crates/bevy_wgpu/src/wgpu_render_pass.rs @@ -2,8 +2,8 @@ use crate::{renderer::WgpuRenderContext, WgpuResourceRefs}; use bevy_asset::Handle; use bevy_render::{ pass::RenderPass, - pipeline::{BindGroupDescriptor, PipelineDescriptor}, - render_resource::{RenderResourceId, RenderResourceSet}, + pipeline::{PipelineDescriptor, BindGroupDescriptorId}, + render_resource::{RenderResourceId, RenderResourceSetId}, renderer::RenderContext, }; use std::ops::Range; @@ -51,33 +51,35 @@ impl<'a> RenderPass for WgpuRenderPass<'a> { fn set_bind_group( &mut self, - bind_group_descriptor: &BindGroupDescriptor, - render_resource_set: &RenderResourceSet, + index: u32, + bind_group_descriptor: BindGroupDescriptorId, + render_resource_set: RenderResourceSetId, + dynamic_uniform_indices: Option<&[u32]>, ) { if let Some(bind_group_info) = self .render_resources .bind_groups - .get(&bind_group_descriptor.id) + .get(&bind_group_descriptor) { - if let Some(wgpu_bind_group) = bind_group_info.bind_groups.get(&render_resource_set.id) + if let Some(wgpu_bind_group) = bind_group_info.bind_groups.get(&render_resource_set) { const EMPTY: &'static [u32] = &[]; - let dynamic_uniform_indices = if let Some(ref dynamic_uniform_indices) = - render_resource_set.dynamic_uniform_indices + let dynamic_uniform_indices = if let Some(dynamic_uniform_indices) = + dynamic_uniform_indices { - dynamic_uniform_indices.as_slice() + dynamic_uniform_indices } else { EMPTY }; log::trace!( "set bind group {:?} {:?}: {:?}", - bind_group_descriptor.id, + bind_group_descriptor, dynamic_uniform_indices, - render_resource_set.id + render_resource_set ); self.render_pass.set_bind_group( - bind_group_descriptor.index, + index, wgpu_bind_group, dynamic_uniform_indices, ); diff --git a/crates/bevy_wgpu/src/wgpu_renderer.rs b/crates/bevy_wgpu/src/wgpu_renderer.rs index 5eca75c9c9..52cd687e77 100644 --- a/crates/bevy_wgpu/src/wgpu_renderer.rs +++ b/crates/bevy_wgpu/src/wgpu_renderer.rs @@ -1,10 +1,10 @@ -use crate::renderer::{ - render_resource_sets_system, WgpuRenderGraphExecutor, WgpuRenderResourceContext, -}; +use crate::renderer::{WgpuRenderGraphExecutor, WgpuRenderResourceContext}; use bevy_app::{EventReader, Events}; use bevy_render::{ - pipeline::update_shader_assignments, + draw::{draw_system, RenderPipelines}, + pipeline::compile_pipelines_system, render_graph::{DependentNodeStager, RenderGraph, RenderGraphStager}, + render_resource::render_resource_sets_system, renderer::RenderResources, }; use bevy_window::{WindowCreated, WindowResized, Windows}; @@ -20,10 +20,7 @@ pub struct WgpuRenderer { } impl WgpuRenderer { - pub async fn new( - window_resized_event_reader: EventReader, - window_created_event_reader: EventReader, - ) -> Self { + pub async fn new() -> Self { let instance = wgpu::Instance::new(); let adapter = instance .request_adapter( @@ -53,8 +50,8 @@ impl WgpuRenderer { instance, device, queue, - window_resized_event_reader, - window_created_event_reader, + window_resized_event_reader: Default::default(), + window_created_event_reader: Default::default(), intialized: false, } } @@ -85,6 +82,7 @@ impl WgpuRenderer { } pub fn run_graph(&mut self, world: &mut World, resources: &mut Resources) { + // TODO: move this to a thread-local system // run systems let mut system_executor = { let mut render_graph = resources.get_mut::().unwrap(); @@ -95,8 +93,12 @@ impl WgpuRenderer { executor.execute(world, resources); } - update_shader_assignments(world, resources); - render_resource_sets_system().run(world, resources); + // TODO: move these to a scheduler + compile_pipelines_system.system().run(world, resources); + render_resource_sets_system.system().run(world, resources); + draw_system:: + .system() + .run(world, resources); let mut render_graph = resources.get_mut::().unwrap(); if let Some(executor) = system_executor.take() { diff --git a/examples/ecs/ecs_guide.rs b/examples/ecs/ecs_guide.rs index e44106e0b4..e76c680dc6 100644 --- a/examples/ecs/ecs_guide.rs +++ b/examples/ecs/ecs_guide.rs @@ -283,13 +283,12 @@ fn stateful_system(mut state: ComMut, player: Com, score: ComMut< // NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes #[allow(dead_code)] fn complex_system(resources: &mut Resources) -> Box { - let mut counter = 0; let game_state = resources.get::().unwrap(); let initial_player_count = game_state.total_players; SystemBuilder::new("complex_system") .read_resource::() .write_resource::() - .read_component::() + .read_component::() // this query is equivalent to the system we saw above: system(player: Com, mut score: ComMut) .with_query(<(Read, Write)>::query()) // this query only returns entities with a Player component that has changed since the last update @@ -303,7 +302,6 @@ fn complex_system(resources: &mut Resources) -> Box { for (player, score) in player_score_query.iter_mut(world) { println!("processed : {} {}", player.name, score.value); - counter += 1; } for player in player_changed_query.iter(world) { diff --git a/examples/shader/shader_custom_material.rs b/examples/shader/shader_custom_material.rs index f0a36e1b11..1c387f6b6f 100644 --- a/examples/shader/shader_custom_material.rs +++ b/examples/shader/shader_custom_material.rs @@ -54,8 +54,6 @@ fn setup( })); render_graph.add_system_node("my_material", AssetUniformNode::::new(true)); - let main_pass: &mut PassNode = render_graph.get_node_mut("main_pass").unwrap(); - main_pass.add_pipeline(pipeline_handle); pipeline_handle }; @@ -70,7 +68,7 @@ fn setup( // cube .add_entity(MeshMaterialEntity:: { mesh: cube_handle, - renderable: Renderable { + render_pipelines: RenderPipelines { pipelines: vec![pipeline_handle], ..Default::default() }, diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index d099879e20..3630ace2f3 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -64,8 +64,6 @@ fn setup( fragment: Some(shaders.add(Shader::from_glsl(ShaderStage::Fragment, FRAGMENT_SHADER))), })); render_graph.add_system_node("my_material", AssetUniformNode::::new(true)); - let main_pass: &mut PassNode = render_graph.get_node_mut("main_pass").unwrap(); - main_pass.add_pipeline(pipeline_handle); pipeline_handle }; @@ -87,7 +85,7 @@ fn setup( // cube .add_entity(MeshMaterialEntity:: { mesh: cube_handle, - renderable: Renderable { + render_pipelines: RenderPipelines { pipelines: vec![pipeline_handle], ..Default::default() }, @@ -98,7 +96,7 @@ fn setup( // cube .add_entity(MeshMaterialEntity:: { mesh: cube_handle, - renderable: Renderable { + render_pipelines: RenderPipelines { pipelines: vec![pipeline_handle], ..Default::default() }, diff --git a/src/prelude.rs b/src/prelude.rs index d6830a2335..5f4d0f963c 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -19,7 +19,7 @@ pub use crate::{ pipeline::PipelineDescriptor, render_graph::{ nodes::{ - AssetUniformNode, CameraNode, PassNode, UniformNode, WindowSwapChainNode, + AssetUniformNode, CameraNode, MainPassNode, UniformNode, WindowSwapChainNode, WindowTextureNode, }, RenderGraph, @@ -27,7 +27,8 @@ pub use crate::{ render_resource::RenderResources, shader::{Shader, ShaderDefs, ShaderStage, ShaderStages}, texture::Texture, - Camera, Color, ColorSource, OrthographicProjection, PerspectiveProjection, Renderable, + draw::{Draw, RenderPipelines}, + Camera, Color, ColorSource, OrthographicProjection, PerspectiveProjection, }, scene::{Scene, SceneSpawner}, sprite::{