diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index bb915965c7..8fa6be9f29 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -27,7 +27,7 @@ use bevy_render::{ color::Color, render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType}, render_phase::{ - batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedPipelinePhaseItem, + batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase, }, render_resource::*, @@ -198,7 +198,7 @@ impl Plugin for CorePipelinePlugin { pub struct Transparent2d { pub sort_key: FloatOrd, pub entity: Entity, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, /// Range in the vertex buffer of this item pub batch_range: Option>, @@ -225,9 +225,9 @@ impl EntityPhaseItem for Transparent2d { } } -impl CachedPipelinePhaseItem for Transparent2d { +impl CachedRenderPipelinePhaseItem for Transparent2d { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } @@ -244,7 +244,7 @@ impl BatchedPhaseItem for Transparent2d { pub struct Opaque3d { pub distance: f32, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, } @@ -270,16 +270,16 @@ impl EntityPhaseItem for Opaque3d { } } -impl CachedPipelinePhaseItem for Opaque3d { +impl CachedRenderPipelinePhaseItem for Opaque3d { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } pub struct AlphaMask3d { pub distance: f32, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, } @@ -305,16 +305,16 @@ impl EntityPhaseItem for AlphaMask3d { } } -impl CachedPipelinePhaseItem for AlphaMask3d { +impl CachedRenderPipelinePhaseItem for AlphaMask3d { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } pub struct Transparent3d { pub distance: f32, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub entity: Entity, pub draw_function: DrawFunctionId, } @@ -340,9 +340,9 @@ impl EntityPhaseItem for Transparent3d { } } -impl CachedPipelinePhaseItem for Transparent3d { +impl CachedRenderPipelinePhaseItem for Transparent3d { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 537026524b..c5bafd333e 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -23,7 +23,7 @@ use bevy_render::{ SetItemPipeline, TrackedRenderPass, }, render_resource::{ - BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, + BindGroup, BindGroupLayout, PipelineCache, RenderPipelineDescriptor, Shader, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, @@ -307,7 +307,7 @@ pub fn queue_material_meshes( transparent_draw_functions: Res>, material_pipeline: Res>, mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, render_materials: Res>, diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index c060574826..e8cab41d39 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -18,7 +18,7 @@ use bevy_render::{ render_asset::RenderAssets, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{ - CachedPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, + CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, EntityRenderCommand, PhaseItem, RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass, }, @@ -1055,7 +1055,7 @@ pub fn queue_shadows( casting_meshes: Query<&Handle, Without>, render_meshes: Res>, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, view_lights: Query<&ViewLightEntities>, mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase)>, point_light_entities: Query<&CubemapVisibleEntities, With>, @@ -1119,7 +1119,7 @@ pub fn queue_shadows( pub struct Shadow { pub distance: f32, pub entity: Entity, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, } @@ -1143,9 +1143,9 @@ impl EntityPhaseItem for Shadow { } } -impl CachedPipelinePhaseItem for Shadow { +impl CachedRenderPipelinePhaseItem for Shadow { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 952ba6bdcd..b4cfefbd8f 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -5,16 +5,14 @@ use bevy_asset::{load_internal_asset, Handle, HandleUntyped}; use bevy_core_pipeline::Opaque3d; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; use bevy_reflect::{Reflect, TypeUuid}; -use bevy_render::mesh::MeshVertexBufferLayout; -use bevy_render::render_resource::{ - PolygonMode, RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError, - SpecializedMeshPipelines, -}; use bevy_render::{ - mesh::Mesh, + mesh::{Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, - render_resource::{RenderPipelineCache, Shader}, + render_resource::{ + PipelineCache, PolygonMode, RenderPipelineDescriptor, Shader, SpecializedMeshPipeline, + SpecializedMeshPipelineError, SpecializedMeshPipelines, + }, view::{ExtractedView, Msaa}, RenderApp, RenderStage, }; @@ -109,8 +107,8 @@ fn queue_wireframes( render_meshes: Res>, wireframe_config: Res, wireframe_pipeline: Res, - mut pipeline_cache: ResMut, - mut specialized_pipelines: ResMut>, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, msaa: Res, mut material_meshes: QuerySet<( QueryState<(Entity, &Handle, &MeshUniform)>, @@ -132,7 +130,7 @@ fn queue_wireframes( if let Some(mesh) = render_meshes.get(mesh_handle) { let key = msaa_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - let pipeline_id = specialized_pipelines.specialize( + let pipeline_id = pipelines.specialize( &mut pipeline_cache, &wireframe_pipeline, key, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7cdd8de681..05429f026c 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -1,3 +1,5 @@ +extern crate core; + pub mod camera; pub mod color; pub mod mesh; @@ -36,7 +38,7 @@ use crate::{ mesh::MeshPlugin, primitives::{CubemapFrusta, Frustum}, render_graph::RenderGraph, - render_resource::{RenderPipelineCache, Shader, ShaderLoader}, + render_resource::{PipelineCache, Shader, ShaderLoader}, renderer::render_system, texture::ImagePlugin, view::{ViewPlugin, WindowRenderPlugin}, @@ -146,12 +148,13 @@ impl Plugin for RenderPlugin { .init_resource::() .register_type::() .register_type::(); - let render_pipeline_cache = RenderPipelineCache::new(device.clone()); + + let pipeline_cache = PipelineCache::new(device.clone()); let asset_server = app.world.resource::().clone(); let mut render_app = App::empty(); let mut extract_stage = - SystemStage::parallel().with_system(RenderPipelineCache::extract_shaders); + SystemStage::parallel().with_system(PipelineCache::extract_shaders); // don't apply buffers when the stage finishes running // extract stage runs on the app world, but the buffers are applied to the render world extract_stage.set_apply_buffers(false); @@ -163,7 +166,7 @@ impl Plugin for RenderPlugin { .add_stage( RenderStage::Render, SystemStage::parallel() - .with_system(RenderPipelineCache::process_pipeline_queue_system) + .with_system(PipelineCache::process_pipeline_queue_system) .with_system(render_system.exclusive_system().at_end()), ) .add_stage(RenderStage::Cleanup, SystemStage::parallel()) @@ -171,7 +174,7 @@ impl Plugin for RenderPlugin { .insert_resource(device) .insert_resource(queue) .insert_resource(adapter_info) - .insert_resource(render_pipeline_cache) + .insert_resource(pipeline_cache) .insert_resource(asset_server) .init_resource::(); diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 73628316f3..5ffca0ea25 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -1,6 +1,6 @@ use crate::{ render_phase::TrackedRenderPass, - render_resource::{CachedPipelineId, RenderPipelineCache}, + render_resource::{CachedRenderPipelineId, PipelineCache}, }; use bevy_app::App; use bevy_ecs::{ @@ -162,8 +162,8 @@ pub trait EntityPhaseItem: PhaseItem { fn entity(&self) -> Entity; } -pub trait CachedPipelinePhaseItem: PhaseItem { - fn cached_pipeline(&self) -> CachedPipelineId; +pub trait CachedRenderPipelinePhaseItem: PhaseItem { + fn cached_pipeline(&self) -> CachedRenderPipelineId; } /// A [`PhaseItem`] that can be batched dynamically. @@ -224,8 +224,8 @@ impl RenderCommand

for E { } pub struct SetItemPipeline; -impl RenderCommand

for SetItemPipeline { - type Param = SRes; +impl RenderCommand

for SetItemPipeline { + type Param = SRes; #[inline] fn render<'w>( _view: Entity, @@ -233,7 +233,10 @@ impl RenderCommand

for SetItemPipeline { pipeline_cache: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { - if let Some(pipeline) = pipeline_cache.into_inner().get(item.cached_pipeline()) { + if let Some(pipeline) = pipeline_cache + .into_inner() + .get_render_pipeline(item.cached_pipeline()) + { pass.set_render_pipeline(pipeline); RenderCommandResult::Success } else { diff --git a/crates/bevy_render/src/render_resource/mod.rs b/crates/bevy_render/src/render_resource/mod.rs index fd60611685..81fa1618df 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -28,13 +28,13 @@ pub use wgpu::{ BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent, BlendFactor, BlendOperation, BlendState, BufferAddress, BufferBinding, BufferBindingType, BufferDescriptor, BufferSize, BufferUsages, ColorTargetState, ColorWrites, - CommandEncoder, CommandEncoderDescriptor, CompareFunction, ComputePassDescriptor, - ComputePipelineDescriptor, DepthBiasState, DepthStencilState, Extent3d, Face, - Features as WgpuFeatures, FilterMode, FragmentState as RawFragmentState, FrontFace, - ImageCopyBuffer, ImageCopyBufferBase, ImageCopyTexture, ImageCopyTextureBase, ImageDataLayout, - ImageSubresourceRange, IndexFormat, Limits as WgpuLimits, LoadOp, MapMode, MultisampleState, - Operations, Origin3d, PipelineLayout, PipelineLayoutDescriptor, PolygonMode, PrimitiveState, - PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment, + CommandEncoder, CommandEncoderDescriptor, CompareFunction, ComputePass, ComputePassDescriptor, + ComputePipelineDescriptor as RawComputePipelineDescriptor, DepthBiasState, DepthStencilState, + Extent3d, Face, Features as WgpuFeatures, FilterMode, FragmentState as RawFragmentState, + FrontFace, ImageCopyBuffer, ImageCopyBufferBase, ImageCopyTexture, ImageCopyTextureBase, + ImageDataLayout, ImageSubresourceRange, IndexFormat, Limits as WgpuLimits, LoadOp, MapMode, + MultisampleState, Operations, Origin3d, PipelineLayout, PipelineLayoutDescriptor, PolygonMode, + PrimitiveState, PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipelineDescriptor as RawRenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, diff --git a/crates/bevy_render/src/render_resource/pipeline.rs b/crates/bevy_render/src/render_resource/pipeline.rs index caa2e41eb7..04e2c772d0 100644 --- a/crates/bevy_render/src/render_resource/pipeline.rs +++ b/crates/bevy_render/src/render_resource/pipeline.rs @@ -168,3 +168,16 @@ pub struct FragmentState { /// The color state of the render targets. pub targets: Vec, } + +/// Describes a compute pipeline. +#[derive(Clone, Debug)] +pub struct ComputePipelineDescriptor { + pub label: Option>, + pub layout: Option>, + /// The compiled shader module for this stage. + pub shader: Handle, + pub shader_defs: Vec, + /// The name of the entry point in the compiled shader. There must be a + /// function with this name in the shader. + pub entry_point: Cow<'static, str>, +} diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 999333514d..484fb97302 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -1,8 +1,10 @@ use crate::{ render_resource::{ - AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ProcessShaderError, - RawFragmentState, RawRenderPipelineDescriptor, RawVertexState, RenderPipeline, - RenderPipelineDescriptor, Shader, ShaderImport, ShaderProcessor, ShaderReflectError, + AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ComputePipeline, + ComputePipelineDescriptor, ProcessShaderError, ProcessedShader, + RawComputePipelineDescriptor, RawFragmentState, RawRenderPipelineDescriptor, + RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, + ShaderProcessor, ShaderReflectError, }, renderer::RenderDevice, RenderWorld, @@ -10,12 +12,61 @@ use crate::{ use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_ecs::event::EventReader; use bevy_ecs::system::{Res, ResMut}; -use bevy_utils::{tracing::error, Entry, HashMap, HashSet}; -use std::{hash::Hash, ops::Deref, sync::Arc}; +use bevy_utils::{default, tracing::error, Entry, HashMap, HashSet}; +use std::{hash::Hash, mem, ops::Deref, sync::Arc}; use thiserror::Error; use wgpu::{PipelineLayoutDescriptor, ShaderModule, VertexBufferLayout as RawVertexBufferLayout}; -use super::ProcessedShader; +enum PipelineDescriptor { + RenderPipelineDescriptor(Box), + ComputePipelineDescriptor(Box), +} + +#[derive(Debug)] +pub enum Pipeline { + RenderPipeline(RenderPipeline), + ComputePipeline(ComputePipeline), +} + +type CachedPipelineId = usize; + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct CachedRenderPipelineId(CachedPipelineId); + +impl CachedRenderPipelineId { + pub const INVALID: Self = CachedRenderPipelineId(usize::MAX); +} + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct CachedComputePipelineId(CachedPipelineId); + +impl CachedComputePipelineId { + pub const INVALID: Self = CachedComputePipelineId(usize::MAX); +} + +struct CachedPipeline { + descriptor: PipelineDescriptor, + state: CachedPipelineState, +} + +#[derive(Debug)] +pub enum CachedPipelineState { + Queued, + Ok(Pipeline), + Err(PipelineCacheError), +} + +impl CachedPipelineState { + pub fn unwrap(&self) -> &Pipeline { + match self { + CachedPipelineState::Ok(pipeline) => pipeline, + CachedPipelineState::Queued => { + panic!("Pipeline has not been compiled yet. It is still in the 'Queued' state.") + } + CachedPipelineState::Err(err) => panic!("{}", err), + } + } +} #[derive(Default)] pub struct ShaderData { @@ -25,13 +76,6 @@ pub struct ShaderData { dependents: HashSet>, } -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub struct CachedPipelineId(usize); - -impl CachedPipelineId { - pub const INVALID: Self = CachedPipelineId(usize::MAX); -} - #[derive(Default)] struct ShaderCache { data: HashMap, ShaderData>, @@ -48,11 +92,11 @@ impl ShaderCache { pipeline: CachedPipelineId, handle: &Handle, shader_defs: &[String], - ) -> Result, RenderPipelineError> { + ) -> Result, PipelineCacheError> { let shader = self .shaders .get(handle) - .ok_or_else(|| RenderPipelineError::ShaderNotLoaded(handle.clone_weak()))?; + .ok_or_else(|| PipelineCacheError::ShaderNotLoaded(handle.clone_weak()))?; let data = self.data.entry(handle.clone_weak()).or_default(); let n_asset_imports = shader .imports() @@ -64,7 +108,7 @@ impl ShaderCache { .filter(|import| matches!(import, ShaderImport::AssetPath(_))) .count(); if n_asset_imports != n_resolved_asset_imports { - return Err(RenderPipelineError::ShaderImportNotYetAvailable); + return Err(PipelineCacheError::ShaderImportNotYetAvailable); } data.pipelines.insert(pipeline); @@ -82,7 +126,7 @@ impl ShaderCache { let module_descriptor = match processed.get_module_descriptor() { Ok(module_descriptor) => module_descriptor, Err(err) => { - return Err(RenderPipelineError::AsModuleDescriptorError(err, processed)); + return Err(PipelineCacheError::AsModuleDescriptorError(err, processed)); } }; entry.insert(Arc::new( @@ -176,13 +220,13 @@ impl LayoutCache { .collect::>(); render_device.create_pipeline_layout(&PipelineLayoutDescriptor { bind_group_layouts: &bind_group_layouts, - ..Default::default() + ..default() }) }) } } -pub struct RenderPipelineCache { +pub struct PipelineCache { layout_cache: LayoutCache, shader_cache: ShaderCache, device: RenderDevice, @@ -190,88 +234,101 @@ pub struct RenderPipelineCache { waiting_pipelines: HashSet, } -struct CachedPipeline { - descriptor: RenderPipelineDescriptor, - state: CachedPipelineState, -} - -#[derive(Debug)] -pub enum CachedPipelineState { - Queued, - Ok(RenderPipeline), - Err(RenderPipelineError), -} - -impl CachedPipelineState { - pub fn unwrap(&self) -> &RenderPipeline { - match self { - CachedPipelineState::Ok(pipeline) => pipeline, - CachedPipelineState::Queued => { - panic!("Pipeline has not been compiled yet. It is still in the 'Queued' state.") - } - CachedPipelineState::Err(err) => panic!("{}", err), - } - } -} - -#[derive(Error, Debug)] -pub enum RenderPipelineError { - #[error( - "Pipeline cound not be compiled because the following shader is not loaded yet: {0:?}" - )] - ShaderNotLoaded(Handle), - #[error(transparent)] - ProcessShaderError(#[from] ProcessShaderError), - #[error("{0}")] - AsModuleDescriptorError(AsModuleDescriptorError, ProcessedShader), - #[error("Shader import not yet available.")] - ShaderImportNotYetAvailable, -} - -impl RenderPipelineCache { +impl PipelineCache { pub fn new(device: RenderDevice) -> Self { Self { device, - layout_cache: Default::default(), - shader_cache: Default::default(), - waiting_pipelines: Default::default(), - pipelines: Default::default(), + layout_cache: default(), + shader_cache: default(), + waiting_pipelines: default(), + pipelines: default(), } } #[inline] - pub fn get_state(&self, id: CachedPipelineId) -> &CachedPipelineState { + pub fn get_render_pipeline_state(&self, id: CachedRenderPipelineId) -> &CachedPipelineState { &self.pipelines[id.0].state } #[inline] - pub fn get_descriptor(&self, id: CachedPipelineId) -> &RenderPipelineDescriptor { - &self.pipelines[id.0].descriptor + pub fn get_compute_pipeline_state(&self, id: CachedComputePipelineId) -> &CachedPipelineState { + &self.pipelines[id.0].state } #[inline] - pub fn get(&self, id: CachedPipelineId) -> Option<&RenderPipeline> { - if let CachedPipelineState::Ok(pipeline) = &self.pipelines[id.0].state { + pub fn get_render_pipeline_descriptor( + &self, + id: CachedRenderPipelineId, + ) -> &RenderPipelineDescriptor { + match &self.pipelines[id.0].descriptor { + PipelineDescriptor::RenderPipelineDescriptor(descriptor) => descriptor, + PipelineDescriptor::ComputePipelineDescriptor(_) => unreachable!(), + } + } + + #[inline] + pub fn get_compute_pipeline_descriptor( + &self, + id: CachedComputePipelineId, + ) -> &ComputePipelineDescriptor { + match &self.pipelines[id.0].descriptor { + PipelineDescriptor::RenderPipelineDescriptor(_) => unreachable!(), + PipelineDescriptor::ComputePipelineDescriptor(descriptor) => descriptor, + } + } + + #[inline] + pub fn get_render_pipeline(&self, id: CachedRenderPipelineId) -> Option<&RenderPipeline> { + if let CachedPipelineState::Ok(Pipeline::RenderPipeline(pipeline)) = + &self.pipelines[id.0].state + { Some(pipeline) } else { None } } - pub fn queue(&mut self, descriptor: RenderPipelineDescriptor) -> CachedPipelineId { - let id = CachedPipelineId(self.pipelines.len()); + #[inline] + pub fn get_compute_pipeline(&self, id: CachedComputePipelineId) -> Option<&ComputePipeline> { + if let CachedPipelineState::Ok(Pipeline::ComputePipeline(pipeline)) = + &self.pipelines[id.0].state + { + Some(pipeline) + } else { + None + } + } + + pub fn queue_render_pipeline( + &mut self, + descriptor: RenderPipelineDescriptor, + ) -> CachedRenderPipelineId { + let id = CachedRenderPipelineId(self.pipelines.len()); self.pipelines.push(CachedPipeline { - descriptor, + descriptor: PipelineDescriptor::RenderPipelineDescriptor(Box::new(descriptor)), state: CachedPipelineState::Queued, }); - self.waiting_pipelines.insert(id); + self.waiting_pipelines.insert(id.0); + id + } + + pub fn queue_compute_pipeline( + &mut self, + descriptor: ComputePipelineDescriptor, + ) -> CachedComputePipelineId { + let id = CachedComputePipelineId(self.pipelines.len()); + self.pipelines.push(CachedPipeline { + descriptor: PipelineDescriptor::ComputePipelineDescriptor(Box::new(descriptor)), + state: CachedPipelineState::Queued, + }); + self.waiting_pipelines.insert(id.0); id } fn set_shader(&mut self, handle: &Handle, shader: &Shader) { let pipelines_to_queue = self.shader_cache.set_shader(handle, shader.clone()); for cached_pipeline in pipelines_to_queue { - self.pipelines[cached_pipeline.0].state = CachedPipelineState::Queued; + self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; self.waiting_pipelines.insert(cached_pipeline); } } @@ -279,28 +336,146 @@ impl RenderPipelineCache { fn remove_shader(&mut self, shader: &Handle) { let pipelines_to_queue = self.shader_cache.remove(shader); for cached_pipeline in pipelines_to_queue { - self.pipelines[cached_pipeline.0].state = CachedPipelineState::Queued; + self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; self.waiting_pipelines.insert(cached_pipeline); } } + fn process_render_pipeline( + &mut self, + id: CachedPipelineId, + descriptor: &RenderPipelineDescriptor, + ) -> CachedPipelineState { + let vertex_module = match self.shader_cache.get( + &self.device, + id, + &descriptor.vertex.shader, + &descriptor.vertex.shader_defs, + ) { + Ok(module) => module, + Err(err) => { + return CachedPipelineState::Err(err); + } + }; + + let fragment_data = if let Some(fragment) = &descriptor.fragment { + let fragment_module = match self.shader_cache.get( + &self.device, + id, + &fragment.shader, + &fragment.shader_defs, + ) { + Ok(module) => module, + Err(err) => { + return CachedPipelineState::Err(err); + } + }; + Some(( + fragment_module, + fragment.entry_point.deref(), + &fragment.targets, + )) + } else { + None + }; + + let vertex_buffer_layouts = descriptor + .vertex + .buffers + .iter() + .map(|layout| RawVertexBufferLayout { + array_stride: layout.array_stride, + attributes: &layout.attributes, + step_mode: layout.step_mode, + }) + .collect::>(); + + let layout = if let Some(layout) = &descriptor.layout { + Some(self.layout_cache.get(&self.device, layout)) + } else { + None + }; + + let descriptor = RawRenderPipelineDescriptor { + multiview: None, + depth_stencil: descriptor.depth_stencil.clone(), + label: descriptor.label.as_deref(), + layout, + multisample: descriptor.multisample, + primitive: descriptor.primitive, + vertex: RawVertexState { + buffers: &vertex_buffer_layouts, + entry_point: descriptor.vertex.entry_point.deref(), + module: &vertex_module, + }, + fragment: fragment_data + .as_ref() + .map(|(module, entry_point, targets)| RawFragmentState { + entry_point, + module, + targets, + }), + }; + + let pipeline = self.device.create_render_pipeline(&descriptor); + + CachedPipelineState::Ok(Pipeline::RenderPipeline(pipeline)) + } + + fn process_compute_pipeline( + &mut self, + id: CachedPipelineId, + descriptor: &ComputePipelineDescriptor, + ) -> CachedPipelineState { + let compute_module = match self.shader_cache.get( + &self.device, + id, + &descriptor.shader, + &descriptor.shader_defs, + ) { + Ok(module) => module, + Err(err) => { + return CachedPipelineState::Err(err); + } + }; + + let layout = if let Some(layout) = &descriptor.layout { + Some(self.layout_cache.get(&self.device, layout)) + } else { + None + }; + + let descriptor = RawComputePipelineDescriptor { + label: descriptor.label.as_deref(), + layout, + module: &compute_module, + entry_point: descriptor.entry_point.as_ref(), + }; + + let pipeline = self.device.create_compute_pipeline(&descriptor); + + CachedPipelineState::Ok(Pipeline::ComputePipeline(pipeline)) + } + pub fn process_queue(&mut self) { - let pipelines = std::mem::take(&mut self.waiting_pipelines); - for id in pipelines { - let state = &mut self.pipelines[id.0]; - match &state.state { + let waiting_pipelines = mem::take(&mut self.waiting_pipelines); + let mut pipelines = mem::take(&mut self.pipelines); + + for id in waiting_pipelines { + let pipeline = &mut pipelines[id]; + match &pipeline.state { CachedPipelineState::Ok(_) => continue, CachedPipelineState::Queued => {} CachedPipelineState::Err(err) => { match err { - RenderPipelineError::ShaderNotLoaded(_) - | RenderPipelineError::ShaderImportNotYetAvailable => { /* retry */ } + PipelineCacheError::ShaderNotLoaded(_) + | PipelineCacheError::ShaderImportNotYetAvailable => { /* retry */ } // shader could not be processed ... retrying won't help - RenderPipelineError::ProcessShaderError(err) => { + PipelineCacheError::ProcessShaderError(err) => { error!("failed to process shader: {}", err); continue; } - RenderPipelineError::AsModuleDescriptorError(err, source) => { + PipelineCacheError::AsModuleDescriptorError(err, source) => { log_shader_error(source, err); continue; } @@ -308,85 +483,21 @@ impl RenderPipelineCache { } } - let descriptor = &state.descriptor; - let vertex_module = match self.shader_cache.get( - &self.device, - id, - &descriptor.vertex.shader, - &descriptor.vertex.shader_defs, - ) { - Ok(module) => module, - Err(err) => { - state.state = CachedPipelineState::Err(err); - self.waiting_pipelines.insert(id); - continue; + pipeline.state = match &pipeline.descriptor { + PipelineDescriptor::RenderPipelineDescriptor(descriptor) => { + self.process_render_pipeline(id, descriptor) + } + PipelineDescriptor::ComputePipelineDescriptor(descriptor) => { + self.process_compute_pipeline(id, descriptor) } }; - let fragment_data = if let Some(fragment) = &descriptor.fragment { - let fragment_module = match self.shader_cache.get( - &self.device, - id, - &fragment.shader, - &fragment.shader_defs, - ) { - Ok(module) => module, - Err(err) => { - state.state = CachedPipelineState::Err(err); - self.waiting_pipelines.insert(id); - continue; - } - }; - Some(( - fragment_module, - fragment.entry_point.deref(), - &fragment.targets, - )) - } else { - None - }; - - let vertex_buffer_layouts = descriptor - .vertex - .buffers - .iter() - .map(|layout| RawVertexBufferLayout { - array_stride: layout.array_stride, - attributes: &layout.attributes, - step_mode: layout.step_mode, - }) - .collect::>(); - - let layout = if let Some(layout) = &descriptor.layout { - Some(self.layout_cache.get(&self.device, layout)) - } else { - None - }; - - let descriptor = RawRenderPipelineDescriptor { - multiview: None, - depth_stencil: descriptor.depth_stencil.clone(), - label: descriptor.label.as_deref(), - layout, - multisample: descriptor.multisample, - primitive: descriptor.primitive, - vertex: RawVertexState { - buffers: &vertex_buffer_layouts, - entry_point: descriptor.vertex.entry_point.deref(), - module: &vertex_module, - }, - fragment: fragment_data - .as_ref() - .map(|(module, entry_point, targets)| RawFragmentState { - entry_point, - module, - targets, - }), - }; - - let pipeline = self.device.create_render_pipeline(&descriptor); - state.state = CachedPipelineState::Ok(pipeline); + if let CachedPipelineState::Err(_) = pipeline.state { + self.waiting_pipelines.insert(id); + } } + + self.pipelines = pipelines; } pub(crate) fn process_pipeline_queue_system(mut cache: ResMut) { @@ -503,6 +614,20 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) { } } +#[derive(Error, Debug)] +pub enum PipelineCacheError { + #[error( + "Pipeline cound not be compiled because the following shader is not loaded yet: {0:?}" + )] + ShaderNotLoaded(Handle), + #[error(transparent)] + ProcessShaderError(#[from] ProcessShaderError), + #[error("{0}")] + AsModuleDescriptorError(AsModuleDescriptorError, ProcessedShader), + #[error("Shader import not yet available.")] + ShaderImportNotYetAvailable, +} + struct ErrorSources<'a> { current: Option<&'a (dyn std::error::Error + 'static)>, } diff --git a/crates/bevy_render/src/render_resource/pipeline_specializer.rs b/crates/bevy_render/src/render_resource/pipeline_specializer.rs index 517c9333da..ee73bf1cf6 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -1,51 +1,74 @@ +use crate::render_resource::CachedComputePipelineId; use crate::{ mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout, MissingVertexAttributeError}, render_resource::{ - CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor, VertexBufferLayout, + CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor, + VertexBufferLayout, }, }; use bevy_utils::{ - hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap, PreHashMapExt, + default, hashbrown::hash_map::RawEntryMut, tracing::error, Entry, HashMap, PreHashMap, + PreHashMapExt, }; -use std::fmt::Debug; -use std::hash::Hash; +use std::{fmt::Debug, hash::Hash}; use thiserror::Error; -pub trait SpecializedPipeline { +pub trait SpecializedRenderPipeline { type Key: Clone + Hash + PartialEq + Eq; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; } -pub struct SpecializedPipelines { - cache: HashMap, +pub struct SpecializedRenderPipelines { + cache: HashMap, } -impl Default for SpecializedPipelines { +impl Default for SpecializedRenderPipelines { fn default() -> Self { - Self { - cache: Default::default(), - } + Self { cache: default() } } } -impl SpecializedPipelines { +impl SpecializedRenderPipelines { pub fn specialize( &mut self, - cache: &mut RenderPipelineCache, + cache: &mut PipelineCache, specialize_pipeline: &S, key: S::Key, - ) -> CachedPipelineId { + ) -> CachedRenderPipelineId { *self.cache.entry(key.clone()).or_insert_with(|| { let descriptor = specialize_pipeline.specialize(key); - cache.queue(descriptor) + cache.queue_render_pipeline(descriptor) }) } } -#[derive(Error, Debug)] -pub enum SpecializedMeshPipelineError { - #[error(transparent)] - MissingVertexAttribute(#[from] MissingVertexAttributeError), +pub trait SpecializedComputePipeline { + type Key: Clone + Hash + PartialEq + Eq; + fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor; +} + +pub struct SpecializedComputePipelines { + cache: HashMap, +} + +impl Default for SpecializedComputePipelines { + fn default() -> Self { + Self { cache: default() } + } +} + +impl SpecializedComputePipelines { + pub fn specialize( + &mut self, + cache: &mut PipelineCache, + specialize_pipeline: &S, + key: S::Key, + ) -> CachedComputePipelineId { + *self.cache.entry(key.clone()).or_insert_with(|| { + let descriptor = specialize_pipeline.specialize(key); + cache.queue_compute_pipeline(descriptor) + }) + } } pub trait SpecializedMeshPipeline { @@ -58,8 +81,9 @@ pub trait SpecializedMeshPipeline { } pub struct SpecializedMeshPipelines { - mesh_layout_cache: PreHashMap>, - vertex_layout_cache: HashMap>, + mesh_layout_cache: + PreHashMap>, + vertex_layout_cache: HashMap>, } impl Default for SpecializedMeshPipelines { @@ -75,11 +99,11 @@ impl SpecializedMeshPipelines { #[inline] pub fn specialize( &mut self, - cache: &mut RenderPipelineCache, + cache: &mut PipelineCache, specialize_pipeline: &S, key: S::Key, layout: &MeshVertexBufferLayout, - ) -> Result { + ) -> Result { let map = self .mesh_layout_cache .get_or_insert_with(layout, Default::default); @@ -113,7 +137,7 @@ impl SpecializedMeshPipelines { Ok(*entry.insert(match layout_map.entry(key) { Entry::Occupied(entry) => { if cfg!(debug_assertions) { - let stored_descriptor = cache.get_descriptor(*entry.get()); + let stored_descriptor = cache.get_render_pipeline_descriptor(*entry.get()); if stored_descriptor != &descriptor { error!("The cached pipeline descriptor for {} is not equal to the generated descriptor for the given key. This means the SpecializePipeline implementation uses 'unused' MeshVertexBufferLayout information to specialize the pipeline. This is not allowed because it would invalidate the pipeline cache.", std::any::type_name::()); } @@ -121,10 +145,16 @@ impl SpecializedMeshPipelines { *entry.into_mut() } Entry::Vacant(entry) => { - *entry.insert(cache.queue(descriptor)) + *entry.insert(cache.queue_render_pipeline(descriptor)) } })) } } } } + +#[derive(Error, Debug)] +pub enum SpecializedMeshPipelineError { + #[error(transparent)] + MissingVertexAttribute(#[from] MissingVertexAttributeError), +} diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index e86f5eaa35..5e8e582fb4 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -35,7 +35,7 @@ use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel}; use bevy_reflect::TypeUuid; use bevy_render::{ render_phase::AddRenderCommand, - render_resource::{Shader, SpecializedPipelines}, + render_resource::{Shader, SpecializedRenderPipelines}, RenderApp, RenderStage, }; @@ -64,7 +64,7 @@ impl Plugin for SpritePlugin { render_app .init_resource::() .init_resource::() - .init_resource::>() + .init_resource::>() .init_resource::() .init_resource::() .init_resource::() diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index b815f7662b..6a900c4604 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -21,7 +21,7 @@ use bevy_render::{ SetItemPipeline, TrackedRenderPass, }, render_resource::{ - BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, + BindGroup, BindGroupLayout, PipelineCache, RenderPipelineDescriptor, Shader, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, @@ -291,7 +291,7 @@ pub fn queue_material2d_meshes( transparent_draw_functions: Res>, material2d_pipeline: Res>, mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, render_materials: Res>, diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 7e3ffe4c97..692cac7b34 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -109,7 +109,7 @@ impl SpritePipelineKey { } } -impl SpecializedPipeline for SpritePipeline { +impl SpecializedRenderPipeline for SpritePipeline { type Key = SpritePipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { @@ -338,8 +338,8 @@ pub fn queue_sprites( mut sprite_meta: ResMut, view_uniforms: Res, sprite_pipeline: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, mut image_bind_groups: ResMut, gpu_images: Res>, msaa: Res, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index dff085aa39..66bf980263 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -64,7 +64,7 @@ pub fn build_ui_render(app: &mut App) { render_app .init_resource::() - .init_resource::>() + .init_resource::>() .init_resource::() .init_resource::() .init_resource::() @@ -403,8 +403,8 @@ pub fn queue_uinodes( mut ui_meta: ResMut, view_uniforms: Res, ui_pipeline: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, mut image_bind_groups: ResMut, gpu_images: Res>, ui_batches: Query<(Entity, &UiBatch)>, diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index dee26dfbc7..f3c3dee5be 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -62,7 +62,7 @@ impl FromWorld for UiPipeline { #[derive(Clone, Copy, Hash, PartialEq, Eq)] pub struct UiPipelineKey {} -impl SpecializedPipeline for UiPipeline { +impl SpecializedRenderPipeline for UiPipeline { type Key = UiPipelineKey; /// FIXME: there are no specialization for now, should this be removed? fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor { diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 23d1577e6a..16b2280c13 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -8,7 +8,7 @@ use bevy_render::{ render_graph::*, render_phase::*, render_resource::{ - CachedPipelineId, LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor, + CachedRenderPipelineId, LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor, }, renderer::*, view::*, @@ -102,7 +102,7 @@ impl Node for UiPassNode { pub struct TransparentUi { pub sort_key: FloatOrd, pub entity: Entity, - pub pipeline: CachedPipelineId, + pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, } @@ -127,9 +127,9 @@ impl EntityPhaseItem for TransparentUi { } } -impl CachedPipelinePhaseItem for TransparentUi { +impl CachedRenderPipelinePhaseItem for TransparentUi { #[inline] - fn cached_pipeline(&self) -> CachedPipelineId { + fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index b09a487a73..e4cc86d886 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -9,9 +9,9 @@ use bevy::{ render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace, - MultisampleState, PolygonMode, PrimitiveState, PrimitiveTopology, RenderPipelineCache, - RenderPipelineDescriptor, SpecializedPipeline, SpecializedPipelines, TextureFormat, - VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, + MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PrimitiveTopology, + RenderPipelineDescriptor, SpecializedRenderPipeline, SpecializedRenderPipelines, + TextureFormat, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, }, texture::BevyDefault, view::VisibleEntities, @@ -125,7 +125,7 @@ impl FromWorld for ColoredMesh2dPipeline { } // We implement `SpecializedPipeline` to customize the default rendering from `Mesh2dPipeline` -impl SpecializedPipeline for ColoredMesh2dPipeline { +impl SpecializedRenderPipeline for ColoredMesh2dPipeline { type Key = Mesh2dPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { @@ -269,7 +269,7 @@ impl Plugin for ColoredMesh2dPlugin { render_app .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_colored_mesh2d) .add_system_to_stage(RenderStage::Queue, queue_colored_mesh2d); } @@ -297,8 +297,8 @@ pub fn extract_colored_mesh2d( pub fn queue_colored_mesh2d( transparent_draw_functions: Res>, colored_mesh2d_pipeline: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With>, diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index 30015b6f3f..abdf5fafe6 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -98,7 +98,7 @@ fn queue_custom( custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, render_meshes: Res>, material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 06b97ac71d..07e3dee47d 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -10,6 +10,7 @@ use bevy::{ }, window::WindowDescriptor, }; +use std::borrow::Cow; const SIZE: (u32, u32) = (1280, 720); const WORKGROUP_SIZE: u32 = 8; @@ -67,7 +68,7 @@ impl Plugin for GameOfLifeComputePlugin { .add_system_to_stage(RenderStage::Queue, queue_bind_group); let mut render_graph = render_app.world.resource_mut::(); - render_graph.add_node("game_of_life", DispatchGameOfLife::default()); + render_graph.add_node("game_of_life", GameOfLifeNode::default()); render_graph .add_node_edge("game_of_life", MAIN_PASS_DEPENDENCIES) .unwrap(); @@ -80,6 +81,7 @@ struct GameOfLifeImageBindGroup(BindGroup); fn extract_game_of_life_image(mut commands: Commands, image: Res) { commands.insert_resource(GameOfLifeImage(image.0.clone())); } + fn queue_bind_group( mut commands: Commands, pipeline: Res, @@ -100,84 +102,96 @@ fn queue_bind_group( } pub struct GameOfLifePipeline { - sim_pipeline: ComputePipeline, - init_pipeline: ComputePipeline, texture_bind_group_layout: BindGroupLayout, + init_pipeline: CachedComputePipelineId, + update_pipeline: CachedComputePipelineId, } impl FromWorld for GameOfLifePipeline { fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); - - let shader_source = include_str!("../../assets/shaders/game_of_life.wgsl"); - let shader = render_device.create_shader_module(&ShaderModuleDescriptor { - label: None, - source: ShaderSource::Wgsl(shader_source.into()), - }); - let texture_bind_group_layout = - render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: None, - entries: &[BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::COMPUTE, - ty: BindingType::StorageTexture { - access: StorageTextureAccess::ReadWrite, - format: TextureFormat::Rgba8Unorm, - view_dimension: TextureViewDimension::D2, - }, - count: None, - }], - }); - - let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor { + world + .resource::() + .create_bind_group_layout(&BindGroupLayoutDescriptor { + label: None, + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::COMPUTE, + ty: BindingType::StorageTexture { + access: StorageTextureAccess::ReadWrite, + format: TextureFormat::Rgba8Unorm, + view_dimension: TextureViewDimension::D2, + }, + count: None, + }], + }); + let shader = world + .resource::() + .load("shaders/game_of_life.wgsl"); + let mut pipeline_cache = world.resource_mut::(); + let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { label: None, - bind_group_layouts: &[&texture_bind_group_layout], - push_constant_ranges: &[], + layout: Some(vec![texture_bind_group_layout.clone()]), + shader: shader.clone(), + shader_defs: vec![], + entry_point: Cow::from("init"), }); - let init_pipeline = render_device.create_compute_pipeline(&ComputePipelineDescriptor { + let update_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { label: None, - layout: Some(&pipeline_layout), - module: &shader, - entry_point: "init", - }); - let sim_pipeline = render_device.create_compute_pipeline(&ComputePipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - module: &shader, - entry_point: "update", + layout: Some(vec![texture_bind_group_layout.clone()]), + shader, + shader_defs: vec![], + entry_point: Cow::from("update"), }); GameOfLifePipeline { - sim_pipeline, - init_pipeline, texture_bind_group_layout, + init_pipeline, + update_pipeline, } } } -enum Initialized { - Default, - No, - Yes, +enum GameOfLifeState { + Loading, + Init, + Update, } -struct DispatchGameOfLife { - initialized: Initialized, +struct GameOfLifeNode { + state: GameOfLifeState, } -impl Default for DispatchGameOfLife { + +impl Default for GameOfLifeNode { fn default() -> Self { Self { - initialized: Initialized::Default, + state: GameOfLifeState::Loading, } } } -impl render_graph::Node for DispatchGameOfLife { - fn update(&mut self, _world: &mut World) { - match self.initialized { - Initialized::Default => self.initialized = Initialized::No, - Initialized::No => self.initialized = Initialized::Yes, - Initialized::Yes => {} + +impl render_graph::Node for GameOfLifeNode { + fn update(&mut self, world: &mut World) { + let pipeline = world.resource::(); + let pipeline_cache = world.resource::(); + + // if the corresponding pipeline has loaded, transition to the next stage + match self.state { + GameOfLifeState::Loading => { + if let CachedPipelineState::Ok(_) = + pipeline_cache.get_compute_pipeline_state(pipeline.init_pipeline) + { + self.state = GameOfLifeState::Init + } + } + GameOfLifeState::Init => { + if let CachedPipelineState::Ok(_) = + pipeline_cache.get_compute_pipeline_state(pipeline.update_pipeline) + { + self.state = GameOfLifeState::Update + } + } + GameOfLifeState::Update => {} } } @@ -187,22 +201,34 @@ impl render_graph::Node for DispatchGameOfLife { render_context: &mut RenderContext, world: &World, ) -> Result<(), render_graph::NodeRunError> { - let pipeline = world.resource::(); let texture_bind_group = &world.resource::().0; + let pipeline_cache = world.resource::(); + let pipeline = world.resource::(); let mut pass = render_context .command_encoder .begin_compute_pass(&ComputePassDescriptor::default()); - if let Initialized::No = self.initialized { - pass.set_pipeline(&pipeline.init_pipeline); - pass.set_bind_group(0, texture_bind_group, &[]); - pass.dispatch(SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1); - } - - pass.set_pipeline(&pipeline.sim_pipeline); pass.set_bind_group(0, texture_bind_group, &[]); - pass.dispatch(SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1); + + // select the pipeline based on the current state + match self.state { + GameOfLifeState::Loading => {} + GameOfLifeState::Init => { + let init_pipeline = pipeline_cache + .get_compute_pipeline(pipeline.init_pipeline) + .unwrap(); + pass.set_pipeline(init_pipeline); + pass.dispatch(SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1); + } + GameOfLifeState::Update => { + let update_pipeline = pipeline_cache + .get_compute_pipeline(pipeline.update_pipeline) + .unwrap(); + pass.set_pipeline(update_pipeline); + pass.dispatch(SIZE.0 / WORKGROUP_SIZE, SIZE.1 / WORKGROUP_SIZE, 1); + } + } Ok(()) } diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index 2e15a52c36..5be3ad6267 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -11,7 +11,7 @@ use bevy::{ render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ - RenderPipelineCache, RenderPipelineDescriptor, SpecializedMeshPipeline, + PipelineCache, RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, view::ExtractedView, @@ -139,7 +139,7 @@ fn queue_custom( custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, material_meshes: Query<(Entity, &Handle, &MeshUniform, &IsRed)>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 75f5a90b11..a30581adb4 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -101,7 +101,7 @@ fn queue_custom( custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipeline_cache: ResMut, meshes: Res>, material_meshes: Query< (Entity, &MeshUniform, &Handle),