diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 8325030519b98..acc01514db321 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -40,7 +40,7 @@ use bevy_render::{ prelude::Color, render_graph::RenderGraph, render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions}, - render_resource::{Shader, SpecializedPipelines}, + render_resource::{Shader, SpecializedRenderPipelines}, view::VisibilitySystems, RenderApp, RenderStage, }; @@ -178,7 +178,7 @@ impl Plugin for PbrPlugin { .init_resource::>() .init_resource::() .init_resource::() - .init_resource::>(); + .init_resource::>(); let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); render_app.add_render_command::(); diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d9ee95446a887..774cb0ea9241d 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -23,8 +23,8 @@ use bevy_render::{ SetItemPipeline, TrackedRenderPass, }, render_resource::{ - BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + BindGroup, BindGroupLayout, PipelineCache, RenderPipelineDescriptor, Shader, + SpecializedRenderPipeline, SpecializedRenderPipelines, }, renderer::RenderDevice, view::{ExtractedView, Msaa, VisibleEntities}, @@ -188,7 +188,7 @@ impl Plugin for MaterialPlugin { .add_render_command::>() .add_render_command::>() .init_resource::>() - .init_resource::>>() + .init_resource::>>() .add_system_to_stage(RenderStage::Queue, queue_material_meshes::); } } @@ -202,7 +202,7 @@ pub struct MaterialPipeline { marker: PhantomData, } -impl SpecializedPipeline for MaterialPipeline { +impl SpecializedRenderPipeline for MaterialPipeline { type Key = (MeshPipelineKey, M::Key); fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { @@ -275,8 +275,8 @@ pub fn queue_material_meshes( alpha_mask_draw_functions: Res>, transparent_draw_functions: Res>, material_pipeline: Res>, - mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + mut pipelines: 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 cc606057f2944..a6bd7937503b7 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -244,7 +244,7 @@ impl ShadowPipelineKey { } } -impl SpecializedPipeline for ShadowPipeline { +impl SpecializedRenderPipeline for ShadowPipeline { type Key = ShadowPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { @@ -1090,8 +1090,8 @@ pub fn queue_shadows( shadow_pipeline: Res, casting_meshes: Query<&Handle, Without>, render_meshes: Res>, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, view_lights: Query<&ViewLightEntities>, mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase)>, point_light_entities: Query<&CubemapVisibleEntities, With>, diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3e8965aa28ee1..4df9d70c93b86 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -411,7 +411,7 @@ impl MeshPipelineKey { } } -impl SpecializedPipeline for MeshPipeline { +impl SpecializedRenderPipeline for MeshPipeline { type Key = MeshPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 9f1308179e518..ad1849d121dfa 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -10,7 +10,9 @@ use bevy_render::{ mesh::Mesh, render_asset::RenderAssets, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, - render_resource::{RenderPipelineCache, Shader, SpecializedPipeline, SpecializedPipelines}, + render_resource::{ + PipelineCache, Shader, SpecializedRenderPipeline, SpecializedRenderPipelines, + }, view::{ExtractedView, Msaa}, RenderApp, RenderStage, }; @@ -35,7 +37,7 @@ impl Plugin for WireframePlugin { render_app .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_wireframes) .add_system_to_stage(RenderStage::Extract, extract_wireframe_config) .add_system_to_stage(RenderStage::Queue, queue_wireframes); @@ -79,7 +81,7 @@ impl FromWorld for WireframePipeline { } } -impl SpecializedPipeline for WireframePipeline { +impl SpecializedRenderPipeline for WireframePipeline { type Key = MeshPipelineKey; fn specialize(&self, key: Self::Key) -> bevy_render::render_resource::RenderPipelineDescriptor { @@ -98,8 +100,8 @@ fn queue_wireframes( render_meshes: Res>, wireframe_config: Res, wireframe_pipeline: Res, - mut pipeline_cache: ResMut, - mut specialized_pipelines: ResMut>, + mut pipeline_cache: ResMut, + mut specialized_pipelines: ResMut>, msaa: Res, mut material_meshes: QuerySet<( QueryState<(Entity, &Handle, &MeshUniform)>, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index d44cb38ee8b8e..5446b913a8f64 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -36,7 +36,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}, @@ -145,12 +145,12 @@ impl Plugin for RenderPlugin { .init_resource::() .register_type::() .register_type::(); - let render_pipeline_cache = RenderPipelineCache::new(device.clone()); + let render_pipeline_cache = PipelineCache::new(device.clone()); let asset_server = app.world.get_resource::().unwrap().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); @@ -162,7 +162,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()) diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 73628316f38d0..ff5d04e299927 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::{CachedPipelineId, PipelineCache}, }; use bevy_app::App; use bevy_ecs::{ @@ -225,7 +225,7 @@ impl RenderCommand

for E { pub struct SetItemPipeline; impl RenderCommand

for SetItemPipeline { - type Param = SRes; + 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 8089199f790da..ec69680caf2d1 100644 --- a/crates/bevy_render/src/render_resource/mod.rs +++ b/crates/bevy_render/src/render_resource/mod.rs @@ -26,13 +26,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 d3bd820053fdc..ab57c62cd0689 100644 --- a/crates/bevy_render/src/render_resource/pipeline.rs +++ b/crates/bevy_render/src/render_resource/pipeline.rs @@ -140,3 +140,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 77ed782eb61cb..618b36479158c 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::{ComputePipeline, RawComputePipelineDescriptor}; use crate::{ render_resource::{ - AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ProcessShaderError, - RawFragmentState, RawRenderPipelineDescriptor, RawVertexState, RenderPipeline, - RenderPipelineDescriptor, Shader, ShaderImport, ShaderProcessor, ShaderReflectError, + AsModuleDescriptorError, BindGroupLayout, BindGroupLayoutId, ComputePipelineDescriptor, + ProcessShaderError, ProcessedShader, RawFragmentState, RawRenderPipelineDescriptor, + RawVertexState, RenderPipeline, RenderPipelineDescriptor, Shader, ShaderImport, + ShaderProcessor, ShaderReflectError, }, renderer::RenderDevice, RenderWorld, @@ -15,14 +17,15 @@ use std::{hash::Hash, ops::Deref, sync::Arc}; use thiserror::Error; use wgpu::{PipelineLayoutDescriptor, ShaderModule, VertexBufferLayout}; -use super::ProcessedShader; +enum PipelineDescriptor { + RenderPipelineDescriptor(RenderPipelineDescriptor), + ComputePipelineDescriptor(ComputePipelineDescriptor), +} -#[derive(Default)] -pub struct ShaderData { - pipelines: HashSet, - processed_shaders: HashMap, Arc>, - resolved_imports: HashMap>, - dependents: HashSet>, +#[derive(Debug)] +pub enum Pipeline { + RenderPipeline(RenderPipeline), + ComputePipeline(ComputePipeline), } #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] @@ -32,6 +35,38 @@ impl CachedPipelineId { pub const INVALID: Self = CachedPipelineId(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 { + pipelines: HashSet, + processed_shaders: HashMap, Arc>, + resolved_imports: HashMap>, + dependents: HashSet>, +} + #[derive(Default)] struct ShaderCache { data: HashMap, ShaderData>, @@ -48,11 +83,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 +99,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 +117,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( @@ -182,7 +217,7 @@ impl LayoutCache { } } -pub struct RenderPipelineCache { +pub struct PipelineCache { layout_cache: LayoutCache, shader_cache: ShaderCache, device: RenderDevice, @@ -190,45 +225,7 @@ 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, @@ -245,18 +242,47 @@ impl RenderPipelineCache { } #[inline] - pub fn get(&self, id: CachedPipelineId) -> Option<&RenderPipeline> { - if let CachedPipelineState::Ok(pipeline) = &self.pipelines[id.0].state { + pub fn get_render_pipeline(&self, id: CachedPipelineId) -> Option<&RenderPipeline> { + if let CachedPipelineState::Ok(Pipeline::RenderPipeline(pipeline)) = + &self.pipelines[id.0].state + { + Some(pipeline) + } else { + None + } + } + + #[inline] + pub fn get_compute_pipeline(&self, id: CachedPipelineId) -> Option<&ComputePipeline> { + if let CachedPipelineState::Ok(Pipeline::ComputePipeline(pipeline)) = + &self.pipelines[id.0].state + { Some(pipeline) } else { None } } - pub fn queue(&mut self, descriptor: RenderPipelineDescriptor) -> CachedPipelineId { + pub fn queue_render_pipeline( + &mut self, + descriptor: RenderPipelineDescriptor, + ) -> CachedPipelineId { + let id = CachedPipelineId(self.pipelines.len()); + self.pipelines.push(CachedPipeline { + descriptor: PipelineDescriptor::RenderPipelineDescriptor(descriptor), + state: CachedPipelineState::Queued, + }); + self.waiting_pipelines.insert(id); + id + } + + pub fn queue_compute_pipeline( + &mut self, + descriptor: ComputePipelineDescriptor, + ) -> CachedPipelineId { let id = CachedPipelineId(self.pipelines.len()); self.pipelines.push(CachedPipeline { - descriptor, + descriptor: PipelineDescriptor::ComputePipelineDescriptor(descriptor), state: CachedPipelineState::Queued, }); self.waiting_pipelines.insert(id); @@ -279,23 +305,141 @@ impl RenderPipelineCache { } } + 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| VertexBufferLayout { + 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 = std::mem::take(&mut self.waiting_pipelines); + let mut pipelines = std::mem::take(&mut self.pipelines); + + for id in waiting_pipelines { + let pipeline = &mut pipelines[id.0]; + 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; } @@ -303,85 +447,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| VertexBufferLayout { - 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) { @@ -498,6 +578,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 a5cb472aa48f8..e9c69d0255233 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -1,12 +1,14 @@ -use crate::render_resource::{CachedPipelineId, RenderPipelineCache, RenderPipelineDescriptor}; +use crate::render_resource::{ + CachedPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor, +}; use bevy_utils::HashMap; use std::hash::Hash; -pub struct SpecializedPipelines { +pub struct SpecializedRenderPipelines { cache: HashMap, } -impl Default for SpecializedPipelines { +impl Default for SpecializedRenderPipelines { fn default() -> Self { Self { cache: Default::default(), @@ -14,21 +16,52 @@ impl Default for SpecializedPipelines { } } -impl SpecializedPipelines { +impl SpecializedRenderPipelines { pub fn specialize( &mut self, - cache: &mut RenderPipelineCache, + cache: &mut PipelineCache, specialize_pipeline: &S, key: S::Key, ) -> CachedPipelineId { *self.cache.entry(key.clone()).or_insert_with(|| { let descriptor = specialize_pipeline.specialize(key); - cache.queue(descriptor) + cache.queue_render_pipeline(descriptor) }) } } -pub trait SpecializedPipeline { +pub trait SpecializedRenderPipeline { type Key: Clone + Hash + PartialEq + Eq; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor; } + +pub struct SpecializedComputePipelines { + cache: HashMap, +} + +impl Default for SpecializedComputePipelines { + fn default() -> Self { + Self { + cache: Default::default(), + } + } +} + +impl SpecializedComputePipelines { + pub fn specialize( + &mut self, + cache: &mut PipelineCache, + specialize_pipeline: &S, + key: S::Key, + ) -> CachedPipelineId { + *self.cache.entry(key.clone()).or_insert_with(|| { + let descriptor = specialize_pipeline.specialize(key); + cache.queue_compute_pipeline(descriptor) + }) + } +} + +pub trait SpecializedComputePipeline { + type Key: Clone + Hash + PartialEq + Eq; + fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor; +} diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index eb70848f04e27..41ffbca949b55 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 0e3ceaae55cc0..89a9d028730a1 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -20,8 +20,8 @@ use bevy_render::{ SetItemPipeline, TrackedRenderPass, }, render_resource::{ - BindGroup, BindGroupLayout, RenderPipelineCache, RenderPipelineDescriptor, Shader, - SpecializedPipeline, SpecializedPipelines, + BindGroup, BindGroupLayout, PipelineCache, RenderPipelineDescriptor, Shader, + SpecializedRenderPipeline, SpecializedRenderPipelines, }, renderer::RenderDevice, view::{ComputedVisibility, Msaa, Visibility, VisibleEntities}, @@ -172,7 +172,7 @@ impl Plugin for Material2dPlugin { render_app .add_render_command::>() .init_resource::>() - .init_resource::>>() + .init_resource::>>() .add_system_to_stage(RenderStage::Queue, queue_material2d_meshes::); } } @@ -186,7 +186,7 @@ pub struct Material2dPipeline { marker: PhantomData, } -impl SpecializedPipeline for Material2dPipeline { +impl SpecializedRenderPipeline for Material2dPipeline { type Key = (Mesh2dPipelineKey, M::Key); fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { @@ -259,8 +259,8 @@ impl EntityRenderCommand pub fn queue_material2d_meshes( transparent_draw_functions: Res>, material2d_pipeline: Res>, - mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>>, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, render_materials: Res>, diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 7468990b47ea8..3b9c00172db6e 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -64,7 +64,7 @@ impl Plugin for Mesh2dRenderPlugin { if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_mesh2d) .add_system_to_stage(RenderStage::Queue, queue_mesh2d_bind_group) .add_system_to_stage(RenderStage::Queue, queue_mesh2d_view_bind_groups); @@ -276,7 +276,7 @@ impl Mesh2dPipelineKey { } } -impl SpecializedPipeline for Mesh2dPipeline { +impl SpecializedRenderPipeline for Mesh2dPipeline { type Key = Mesh2dPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 4eceaf2bdfc87..a7ac40288f571 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 { @@ -347,8 +347,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 947eda82c7e08..3f3962dfbcf79 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -73,7 +73,7 @@ pub fn build_ui_render(app: &mut App) { render_app .init_resource::() - .init_resource::>() + .init_resource::>() .init_resource::() .init_resource::() .init_resource::() @@ -416,8 +416,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 4538dc922b131..7fa13d2740fa4 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/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 371645839ac08..6d48ae4c86cf2 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -9,9 +9,10 @@ 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, - VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, + MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PrimitiveTopology, + RenderPipelineDescriptor, SpecializedRenderPipeline, SpecializedRenderPipelines, + TextureFormat, VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, + VertexStepMode, }, texture::BevyDefault, view::VisibleEntities, @@ -125,7 +126,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 { @@ -281,7 +282,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); } @@ -309,8 +310,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 6e8f4b00af0fc..a3942058ef7ef 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -66,7 +66,7 @@ impl Plugin for CustomMaterialPlugin { bind_group: None, }) .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_time) .add_system_to_stage(RenderStage::Extract, extract_custom_material) .add_system_to_stage(RenderStage::Prepare, prepare_time) @@ -94,8 +94,8 @@ fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, material_meshes: Query<(Entity, &MeshUniform), (With>, With)>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { @@ -207,7 +207,7 @@ impl FromWorld for CustomPipeline { } } -impl SpecializedPipeline for CustomPipeline { +impl SpecializedRenderPipeline for CustomPipeline { type Key = MeshPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 9ad3db0b5bfe1..0610eaff2b54e 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -10,10 +10,27 @@ use bevy::{ }, window::WindowDescriptor, }; +use std::borrow::Cow; +use std::ops::Deref; const SIZE: (u32, u32) = (1280, 720); const WORKGROUP_SIZE: u32 = 8; +// the layout descriptor of the bind group of game of life compute shader +const BIND_GROUP_LAYOUT: BindGroupLayoutDescriptor = 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, + }], +}; + fn main() { App::new() .insert_resource(ClearColor(Color::BLACK)) @@ -63,6 +80,7 @@ impl Plugin for GameOfLifeComputePlugin { let render_app = app.sub_app_mut(RenderApp); render_app .init_resource::() + .init_resource::>() .add_system_to_stage(RenderStage::Extract, extract_game_of_life_image) .add_system_to_stage(RenderStage::Queue, queue_bind_group); @@ -80,6 +98,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, @@ -99,85 +118,107 @@ fn queue_bind_group( commands.insert_resource(GameOfLifeImageBindGroup(bind_group)); } +#[derive(Clone, Hash, PartialEq, Eq)] +pub enum GameOfLifePipelineKey { + Init, + Update, +} + pub struct GameOfLifePipeline { - sim_pipeline: ComputePipeline, - init_pipeline: ComputePipeline, texture_bind_group_layout: BindGroupLayout, + shader: Handle, } impl FromWorld for GameOfLifePipeline { fn from_world(world: &mut World) -> Self { let render_device = world.get_resource::().unwrap(); + let asset_server = world.get_resource::().unwrap(); - 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 { - label: None, - bind_group_layouts: &[&texture_bind_group_layout], - push_constant_ranges: &[], - }); - let init_pipeline = render_device.create_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", - }); + let texture_bind_group_layout = render_device.create_bind_group_layout(&BIND_GROUP_LAYOUT); + let shader = asset_server.load("shaders/game_of_life.wgsl"); GameOfLifePipeline { - sim_pipeline, - init_pipeline, texture_bind_group_layout, + shader, } } } -enum Initialized { - Default, - No, - Yes, +impl SpecializedComputePipeline for GameOfLifePipeline { + type Key = GameOfLifePipelineKey; + + fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor { + let entry_point = match key { + GameOfLifePipelineKey::Init => "init", + GameOfLifePipelineKey::Update => "update", + }; + + ComputePipelineDescriptor { + label: None, + layout: Some(vec![self.texture_bind_group_layout.clone()]), + shader: self.shader.clone(), + shader_defs: vec![], + entry_point: Cow::from(entry_point), + } + } +} + +enum GameOfLifeState { + NotReady, + Init, + Update, } struct DispatchGameOfLife { - initialized: Initialized, + state: GameOfLifeState, + init_pipeline: CachedPipelineId, + update_pipeline: CachedPipelineId, } + impl Default for DispatchGameOfLife { fn default() -> Self { Self { - initialized: Initialized::Default, + state: GameOfLifeState::NotReady, + init_pipeline: CachedPipelineId::INVALID, + update_pipeline: CachedPipelineId::INVALID, } } } + 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 => {} + fn update(&mut self, world: &mut World) { + let world = world.cell(); + + let mut pipelines = world + .get_resource_mut::>() + .unwrap(); + let mut pipeline_cache = world.get_resource_mut::().unwrap(); + let game_of_life_pipeline = world.get_resource::().unwrap(); + + self.init_pipeline = pipelines.specialize( + &mut pipeline_cache, + game_of_life_pipeline.deref(), + GameOfLifePipelineKey::Init, + ); + self.update_pipeline = pipelines.specialize( + &mut pipeline_cache, + game_of_life_pipeline.deref(), + GameOfLifePipelineKey::Update, + ); + + // if the corresponding pipeline has loaded, transition to the next stage + match self.state { + GameOfLifeState::NotReady => { + if let CachedPipelineState::Ok(_) = pipeline_cache.get_state(self.init_pipeline) { + self.state = GameOfLifeState::Init + } + } + GameOfLifeState::Init => { + if let CachedPipelineState::Ok(_) = pipeline_cache.get_state(self.update_pipeline) { + self.state = GameOfLifeState::Update + } + } + GameOfLifeState::Update => {} } } @@ -187,22 +228,33 @@ impl render_graph::Node for DispatchGameOfLife { render_context: &mut RenderContext, world: &World, ) -> Result<(), render_graph::NodeRunError> { - let pipeline = world.get_resource::().unwrap(); let texture_bind_group = &world.get_resource::().unwrap().0; + let pipeline_cache = world.get_resource::().unwrap(); 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::NotReady => {} + GameOfLifeState::Init => { + let init_pipeline = pipeline_cache + .get_compute_pipeline(self.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(self.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 bec75411f0a3e..cbccd7e3c0f95 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -10,8 +10,8 @@ use bevy::{ render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ - RenderPipelineCache, RenderPipelineDescriptor, SpecializedPipeline, - SpecializedPipelines, + PipelineCache, RenderPipelineDescriptor, SpecializedRenderPipeline, + SpecializedRenderPipelines, }, view::ExtractedView, RenderApp, RenderStage, @@ -26,7 +26,7 @@ impl Plugin for IsRedPlugin { app.sub_app_mut(RenderApp) .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom); } } @@ -98,7 +98,7 @@ impl FromWorld for IsRedPipeline { } } -impl SpecializedPipeline for IsRedPipeline { +impl SpecializedRenderPipeline for IsRedPipeline { type Key = (IsRed, MeshPipelineKey); fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor { @@ -133,8 +133,8 @@ fn queue_custom( render_meshes: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: 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 4c88cd17481e4..7b8ccc7e71e97 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -81,7 +81,7 @@ impl Plugin for CustomMaterialPlugin { app.sub_app_mut(RenderApp) .add_render_command::() .init_resource::() - .init_resource::>() + .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom) .add_system_to_stage(RenderStage::Prepare, prepare_instance_buffers); } @@ -99,8 +99,8 @@ fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, msaa: Res, - mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + mut pipelines: ResMut>, + mut pipeline_cache: ResMut, material_meshes: Query< (Entity, &MeshUniform), (With>, With), @@ -175,7 +175,7 @@ impl FromWorld for CustomPipeline { } } -impl SpecializedPipeline for CustomPipeline { +impl SpecializedRenderPipeline for CustomPipeline { type Key = MeshPipelineKey; fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {