From 9a4cc42b38e36f70bee6f81891bb653f090d4042 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Fri, 12 Nov 2021 22:27:17 +0000 Subject: [PATCH] EntityRenderCommand and PhaseItemRenderCommand (#3111) Adds new `EntityRenderCommand`, `EntityPhaseItem`, and `CachedPipelinePhaseItem` traits to make it possible to reuse RenderCommands across phases. This should be helpful for features like #3072 . It also makes the trait impls slightly less generic-ey in the common cases. This also fixes the custom shader examples to account for the recent Frustum Culling and MSAA changes (the UX for these things will be improved later). --- assets/shaders/custom.wgsl | 4 +- examples/shader/custom_shader_pipelined.rs | 67 +++++++----- examples/shader/shader_defs_pipelined.rs | 23 ++-- pipelined/bevy_core_pipeline/src/lib.rs | 42 ++------ pipelined/bevy_pbr2/src/lib.rs | 9 +- pipelined/bevy_pbr2/src/render/light.rs | 102 ++++++++---------- pipelined/bevy_pbr2/src/render/mod.rs | 28 ++--- .../bevy_render2/src/render_phase/draw.rs | 59 +++++++++- 8 files changed, 184 insertions(+), 150 deletions(-) diff --git a/assets/shaders/custom.wgsl b/assets/shaders/custom.wgsl index cd1fc4fdb5..8fef36d303 100644 --- a/assets/shaders/custom.wgsl +++ b/assets/shaders/custom.wgsl @@ -11,7 +11,7 @@ var view: View; struct Mesh { transform: mat4x4; }; -[[group(2), binding(0)]] +[[group(1), binding(0)]] var mesh: Mesh; struct Vertex { @@ -37,7 +37,7 @@ fn vertex(vertex: Vertex) -> VertexOutput { struct CustomMaterial { color: vec4; }; -[[group(1), binding(0)]] +[[group(2), binding(0)]] var material: CustomMaterial; [[stage(fragment)]] diff --git a/examples/shader/custom_shader_pipelined.rs b/examples/shader/custom_shader_pipelined.rs index 1ef47b644c..9af6b39dcd 100644 --- a/examples/shader/custom_shader_pipelined.rs +++ b/examples/shader/custom_shader_pipelined.rs @@ -1,5 +1,5 @@ use bevy::{ - core_pipeline::{SetItemPipeline, Transparent3d}, + core_pipeline::Transparent3d, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, ecs::{ prelude::*, @@ -19,11 +19,12 @@ use bevy::{ render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets}, render_component::ExtractComponentPlugin, render_phase::{ - AddRenderCommand, DrawFunctions, RenderCommand, RenderPhase, TrackedRenderPass, + AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderPhase, SetItemPipeline, + TrackedRenderPass, }, render_resource::*, renderer::RenderDevice, - view::ExtractedView, + view::{ComputedVisibility, ExtractedView, Msaa, Visibility}, RenderApp, RenderStage, }, PipelinedDefaultPlugins, @@ -51,6 +52,8 @@ fn setup( meshes.add(Mesh::from(shape::Cube { size: 1.0 })), Transform::from_xyz(0.0, 0.5, 0.0), GlobalTransform::default(), + Visibility::default(), + ComputedVisibility::default(), materials.add(CustomMaterial { color: Color::GREEN, }), @@ -118,21 +121,36 @@ impl Plugin for CustomMaterialPlugin { app.sub_app(RenderApp) .add_render_command::() .init_resource::() + .init_resource::>() .add_system_to_stage(RenderStage::Queue, queue_custom); } } pub struct CustomPipeline { material_layout: BindGroupLayout, - pipeline: CachedPipelineId, + shader: Handle, + pbr_pipeline: PbrPipeline, +} + +impl SpecializedPipeline for CustomPipeline { + type Key = PbrPipelineKey; + + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + let mut descriptor = self.pbr_pipeline.specialize(key); + descriptor.vertex.shader = self.shader.clone(); + descriptor.fragment.as_mut().unwrap().shader = self.shader.clone(); + descriptor.layout = Some(vec![ + self.pbr_pipeline.view_layout.clone(), + self.pbr_pipeline.mesh_layout.clone(), + self.material_layout.clone(), + ]); + descriptor + } } impl FromWorld for CustomPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let asset_server = world.get_resource::().unwrap(); - let shader = asset_server.load("shaders/custom.wgsl"); - let render_device = world.get_resource::().unwrap(); let material_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { @@ -148,28 +166,22 @@ impl FromWorld for CustomPipeline { label: None, }); - let pbr_pipeline = world.get_resource::().unwrap(); - let mut descriptor = pbr_pipeline.specialize(PbrPipelineKey::empty()); - descriptor.vertex.shader = shader.clone(); - descriptor.fragment.as_mut().unwrap().shader = shader; - descriptor.layout = Some(vec![ - pbr_pipeline.view_layout.clone(), - material_layout.clone(), - pbr_pipeline.mesh_layout.clone(), - ]); - - let mut pipeline_cache = world.get_resource_mut::().unwrap(); CustomPipeline { - pipeline: pipeline_cache.queue(descriptor), + pbr_pipeline: world.get_resource::().unwrap().clone(), + shader: asset_server.load("shaders/custom.wgsl"), material_layout, } } } +#[allow(clippy::too_many_arguments)] pub fn queue_custom( transparent_3d_draw_functions: Res>, materials: Res>, custom_pipeline: Res, + mut pipeline_cache: ResMut, + mut specialized_pipelines: ResMut>, + msaa: Res, material_meshes: Query<(Entity, &Handle, &MeshUniform), With>>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, ) { @@ -177,6 +189,7 @@ pub fn queue_custom( .read() .get_id::() .unwrap(); + let key = PbrPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); @@ -184,7 +197,11 @@ pub fn queue_custom( if materials.contains_key(material_handle) { transparent_phase.add(Transparent3d { entity, - pipeline: custom_pipeline.pipeline, + pipeline: specialized_pipelines.specialize( + &mut pipeline_cache, + &custom_pipeline, + key, + ), draw_function: draw_custom, distance: view_row_2.dot(mesh_uniform.transform.col(3)), }); @@ -196,25 +213,25 @@ pub fn queue_custom( type DrawCustom = ( SetItemPipeline, SetMeshViewBindGroup<0>, + SetTransformBindGroup<1>, SetCustomMaterialBindGroup, - SetTransformBindGroup<2>, DrawMesh, ); struct SetCustomMaterialBindGroup; -impl RenderCommand for SetCustomMaterialBindGroup { +impl EntityRenderCommand for SetCustomMaterialBindGroup { type Param = ( SRes>, SQuery>>, ); fn render<'w>( _view: Entity, - item: &Transparent3d, + item: Entity, (materials, query): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) { - let material_handle = query.get(item.entity).unwrap(); + let material_handle = query.get(item).unwrap(); let material = materials.into_inner().get(material_handle).unwrap(); - pass.set_bind_group(1, &material.bind_group, &[]); + pass.set_bind_group(2, &material.bind_group, &[]); } } diff --git a/examples/shader/shader_defs_pipelined.rs b/examples/shader/shader_defs_pipelined.rs index c890d26eac..c34604007a 100644 --- a/examples/shader/shader_defs_pipelined.rs +++ b/examples/shader/shader_defs_pipelined.rs @@ -1,5 +1,5 @@ use bevy::{ - core_pipeline::{SetItemPipeline, Transparent3d}, + core_pipeline::Transparent3d, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, ecs::prelude::*, math::Vec3, @@ -12,9 +12,9 @@ use bevy::{ camera::PerspectiveCameraBundle, mesh::{shape, Mesh}, render_component::{ExtractComponent, ExtractComponentPlugin}, - render_phase::{AddRenderCommand, DrawFunctions, RenderPhase}, + render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::*, - view::ExtractedView, + view::{ComputedVisibility, ExtractedView, Msaa, Visibility}, RenderApp, RenderStage, }, PipelinedDefaultPlugins, @@ -64,6 +64,8 @@ fn setup(mut commands: Commands, mut meshes: ResMut>) { IsRed(true), Transform::from_xyz(-1.0, 0.5, 0.0), GlobalTransform::default(), + Visibility::default(), + ComputedVisibility::default(), )); // blue cube @@ -72,6 +74,8 @@ fn setup(mut commands: Commands, mut meshes: ResMut>) { IsRed(false), Transform::from_xyz(1.0, 0.5, 0.0), GlobalTransform::default(), + Visibility::default(), + ComputedVisibility::default(), )); // camera @@ -99,14 +103,14 @@ impl FromWorld for IsRedPipeline { } impl SpecializedPipeline for IsRedPipeline { - type Key = IsRed; + type Key = (IsRed, PbrPipelineKey); - fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + fn specialize(&self, (is_red, pbr_pipeline_key): Self::Key) -> RenderPipelineDescriptor { let mut shader_defs = Vec::new(); - if key.0 { + if is_red.0 { shader_defs.push("IS_RED".to_string()); } - let mut descriptor = self.pbr_pipeline.specialize(PbrPipelineKey::empty()); + let mut descriptor = self.pbr_pipeline.specialize(pbr_pipeline_key); descriptor.vertex.shader = self.shader.clone(); descriptor.vertex.shader_defs = shader_defs.clone(); let fragment = descriptor.fragment.as_mut().unwrap(); @@ -130,6 +134,7 @@ type DrawIsRed = ( fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, + msaa: Res, mut pipelines: ResMut>, mut pipeline_cache: ResMut, material_meshes: Query<(Entity, &MeshUniform, &IsRed), With>>, @@ -139,11 +144,13 @@ fn queue_custom( .read() .get_id::() .unwrap(); + let key = PbrPipelineKey::from_msaa_samples(msaa.samples); for (view, mut transparent_phase) in views.iter_mut() { let view_matrix = view.transform.compute_matrix(); let view_row_2 = view_matrix.row(2); for (entity, mesh_uniform, is_red) in material_meshes.iter() { - let pipeline = pipelines.specialize(&mut pipeline_cache, &custom_pipeline, *is_red); + let pipeline = + pipelines.specialize(&mut pipeline_cache, &custom_pipeline, (*is_red, key)); transparent_phase.add(Transparent3d { entity, pipeline, diff --git a/pipelined/bevy_core_pipeline/src/lib.rs b/pipelined/bevy_core_pipeline/src/lib.rs index bb38fab3bb..5d9be70e75 100644 --- a/pipelined/bevy_core_pipeline/src/lib.rs +++ b/pipelined/bevy_core_pipeline/src/lib.rs @@ -8,17 +8,14 @@ pub use main_pass_driver::*; use bevy_app::{App, Plugin}; use bevy_core::FloatOrd; -use bevy_ecs::{ - prelude::*, - system::{lifetimeless::SRes, SystemParamItem}, -}; +use bevy_ecs::prelude::*; use bevy_render2::{ camera::{ActiveCameras, CameraPlugin}, color::Color, render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType}, render_phase::{ - sort_phase_system, DrawFunctionId, DrawFunctions, PhaseItem, RenderCommand, RenderPhase, - TrackedRenderPass, + sort_phase_system, CachedPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, + PhaseItem, RenderPhase, }, render_resource::*, renderer::RenderDevice, @@ -171,38 +168,17 @@ impl PhaseItem for Transparent3d { } } -pub struct SetItemPipeline; -impl RenderCommand for SetItemPipeline { - type Param = SRes; +impl EntityPhaseItem for Transparent3d { #[inline] - fn render<'w>( - _view: Entity, - item: &Transparent3d, - pipeline_cache: SystemParamItem<'w, '_, Self::Param>, - pass: &mut TrackedRenderPass<'w>, - ) { - let pipeline = pipeline_cache - .into_inner() - .get_state(item.pipeline) - .unwrap(); - pass.set_render_pipeline(pipeline); + fn entity(&self) -> Entity { + self.entity } } -impl RenderCommand for SetItemPipeline { - type Param = SRes; +impl CachedPipelinePhaseItem for Transparent3d { #[inline] - fn render<'w>( - _view: Entity, - item: &Transparent2d, - pipeline_cache: SystemParamItem<'w, '_, Self::Param>, - pass: &mut TrackedRenderPass<'w>, - ) { - let pipeline = pipeline_cache - .into_inner() - .get_state(item.pipeline) - .unwrap(); - pass.set_render_pipeline(pipeline); + fn cached_pipeline(&self) -> CachedPipelineId { + self.pipeline } } diff --git a/pipelined/bevy_pbr2/src/lib.rs b/pipelined/bevy_pbr2/src/lib.rs index 67c5c8e302..7b17c99c1b 100644 --- a/pipelined/bevy_pbr2/src/lib.rs +++ b/pipelined/bevy_pbr2/src/lib.rs @@ -108,15 +108,10 @@ impl Plugin for PbrPlugin { .init_resource::>() .init_resource::>(); - let draw_shadow_mesh = DrawShadowMesh::new(&mut render_app.world); let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); render_app.add_render_command::(); - let render_world = render_app.world.cell(); - let draw_functions = render_world - .get_resource::>() - .unwrap(); - draw_functions.write().add(draw_shadow_mesh); - let mut graph = render_world.get_resource_mut::().unwrap(); + render_app.add_render_command::(); + let mut graph = render_app.world.get_resource_mut::().unwrap(); let draw_3d_graph = graph .get_sub_graph_mut(bevy_core_pipeline::draw_3d_graph::NAME) .unwrap(); diff --git a/pipelined/bevy_pbr2/src/render/light.rs b/pipelined/bevy_pbr2/src/render/light.rs index e839f4785a..90c474c8d5 100644 --- a/pipelined/bevy_pbr2/src/render/light.rs +++ b/pipelined/bevy_pbr2/src/render/light.rs @@ -1,6 +1,6 @@ use crate::{ - AmbientLight, CubemapVisibleEntities, DirectionalLight, DirectionalLightShadowMap, MeshUniform, - NotShadowCaster, PbrPipeline, PointLight, PointLightShadowMap, TransformBindGroup, + AmbientLight, CubemapVisibleEntities, DirectionalLight, DirectionalLightShadowMap, DrawMesh, + NotShadowCaster, PbrPipeline, PointLight, PointLightShadowMap, SetTransformBindGroup, SHADOW_SHADER_HANDLE, }; use bevy_asset::Handle; @@ -8,7 +8,7 @@ use bevy_core::FloatOrd; use bevy_core_pipeline::Transparent3d; use bevy_ecs::{ prelude::*, - system::{lifetimeless::*, SystemState}, + system::{lifetimeless::*, SystemParamItem}, }; use bevy_math::{const_vec3, Mat4, Vec3, Vec4}; use bevy_render2::{ @@ -16,10 +16,10 @@ use bevy_render2::{ color::Color, mesh::Mesh, render_asset::RenderAssets, - render_component::DynamicUniformIndex, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{ - Draw, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, TrackedRenderPass, + CachedPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem, + EntityRenderCommand, PhaseItem, RenderPhase, SetItemPipeline, TrackedRenderPass, }, render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, @@ -796,6 +796,19 @@ impl PhaseItem for Shadow { } } +impl EntityPhaseItem for Shadow { + fn entity(&self) -> Entity { + self.entity + } +} + +impl CachedPipelinePhaseItem for Shadow { + #[inline] + fn cached_pipeline(&self) -> CachedPipelineId { + self.pipeline + } +} + pub struct ShadowPassNode { main_view_query: QueryState<&'static ViewLights>, view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase)>, @@ -865,63 +878,32 @@ impl Node for ShadowPassNode { } } -pub struct DrawShadowMesh { - params: SystemState<( - SRes, - SRes, - SRes, - SRes>, - SQuery<(Read>, Read>)>, - SQuery>, - )>, -} +pub type DrawShadowMesh = ( + SetItemPipeline, + SetShadowViewBindGroup<0>, + SetTransformBindGroup<1>, + DrawMesh, +); -impl DrawShadowMesh { - pub fn new(world: &mut World) -> Self { - Self { - params: SystemState::new(world), - } - } -} - -impl Draw for DrawShadowMesh { - fn draw<'w>( - &mut self, - world: &'w World, - pass: &mut TrackedRenderPass<'w>, +pub struct SetShadowViewBindGroup; +impl EntityRenderCommand for SetShadowViewBindGroup { + type Param = (SRes, SQuery>); + #[inline] + fn render<'w>( view: Entity, - item: &Shadow, + _item: Entity, + (light_meta, view_query): SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, ) { - let (pipeline_cache, light_meta, transform_bind_group, meshes, items, views) = - self.params.get(world); - let (transform_index, mesh_handle) = items.get(item.entity).unwrap(); - let view_uniform_offset = views.get(view).unwrap(); - if let Some(pipeline) = pipeline_cache.into_inner().get(item.pipeline) { - pass.set_render_pipeline(pipeline); - pass.set_bind_group( - 0, - light_meta - .into_inner() - .shadow_view_bind_group - .as_ref() - .unwrap(), - &[view_uniform_offset.offset], - ); - - pass.set_bind_group( - 1, - &transform_bind_group.into_inner().value, - &[transform_index.index()], - ); - - let gpu_mesh = meshes.into_inner().get(mesh_handle).unwrap(); - pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); - if let Some(index_info) = &gpu_mesh.index_info { - pass.set_index_buffer(index_info.buffer.slice(..), 0, index_info.index_format); - pass.draw_indexed(0..index_info.count, 0, 0..1); - } else { - panic!("non-indexed drawing not supported yet") - } - } + let view_uniform_offset = view_query.get(view).unwrap(); + pass.set_bind_group( + I, + light_meta + .into_inner() + .shadow_view_bind_group + .as_ref() + .unwrap(), + &[view_uniform_offset.offset], + ); } } diff --git a/pipelined/bevy_pbr2/src/render/mod.rs b/pipelined/bevy_pbr2/src/render/mod.rs index c6872af3d1..c1d08028c2 100644 --- a/pipelined/bevy_pbr2/src/render/mod.rs +++ b/pipelined/bevy_pbr2/src/render/mod.rs @@ -7,7 +7,7 @@ use crate::{ PBR_SHADER_HANDLE, }; use bevy_asset::Handle; -use bevy_core_pipeline::{SetItemPipeline, Transparent3d}; +use bevy_core_pipeline::Transparent3d; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, @@ -17,7 +17,9 @@ use bevy_render2::{ mesh::Mesh, render_asset::RenderAssets, render_component::{ComponentUniforms, DynamicUniformIndex}, - render_phase::{DrawFunctions, RenderCommand, RenderPhase, TrackedRenderPass}, + render_phase::{ + DrawFunctions, EntityRenderCommand, RenderPhase, SetItemPipeline, TrackedRenderPass, + }, render_resource::*, renderer::{RenderDevice, RenderQueue}, texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo}, @@ -734,7 +736,7 @@ pub type DrawPbr = ( ); pub struct SetMeshViewBindGroup; -impl RenderCommand for SetMeshViewBindGroup { +impl EntityRenderCommand for SetMeshViewBindGroup { type Param = SQuery<( Read, Read, @@ -743,7 +745,7 @@ impl RenderCommand for SetMeshViewBindGroup { #[inline] fn render<'w>( view: Entity, - _item: &Transparent3d, + _item: Entity, view_query: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) { @@ -757,7 +759,7 @@ impl RenderCommand for SetMeshViewBindGroup { } pub struct SetTransformBindGroup; -impl RenderCommand for SetTransformBindGroup { +impl EntityRenderCommand for SetTransformBindGroup { type Param = ( SRes, SQuery>>, @@ -765,11 +767,11 @@ impl RenderCommand for SetTransformBindGroup { #[inline] fn render<'w>( _view: Entity, - item: &Transparent3d, + item: Entity, (transform_bind_group, mesh_query): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) { - let transform_index = mesh_query.get(item.entity).unwrap(); + let transform_index = mesh_query.get(item).unwrap(); pass.set_bind_group( I, &transform_bind_group.into_inner().value, @@ -779,7 +781,7 @@ impl RenderCommand for SetTransformBindGroup { } pub struct SetStandardMaterialBindGroup; -impl RenderCommand for SetStandardMaterialBindGroup { +impl EntityRenderCommand for SetStandardMaterialBindGroup { type Param = ( SRes>, SQuery>>, @@ -787,11 +789,11 @@ impl RenderCommand for SetStandardMaterialBindGro #[inline] fn render<'w>( _view: Entity, - item: &Transparent3d, + item: Entity, (materials, handle_query): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) { - let handle = handle_query.get(item.entity).unwrap(); + let handle = handle_query.get(item).unwrap(); let materials = materials.into_inner(); let material = materials.get(handle).unwrap(); @@ -800,16 +802,16 @@ impl RenderCommand for SetStandardMaterialBindGro } pub struct DrawMesh; -impl RenderCommand for DrawMesh { +impl EntityRenderCommand for DrawMesh { type Param = (SRes>, SQuery>>); #[inline] fn render<'w>( _view: Entity, - item: &Transparent3d, + item: Entity, (meshes, mesh_query): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) { - let mesh_handle = mesh_query.get(item.entity).unwrap(); + let mesh_handle = mesh_query.get(item).unwrap(); let gpu_mesh = meshes.into_inner().get(mesh_handle).unwrap(); pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..)); if let Some(index_info) = &gpu_mesh.index_info { diff --git a/pipelined/bevy_render2/src/render_phase/draw.rs b/pipelined/bevy_render2/src/render_phase/draw.rs index 02272eb896..28522bfd19 100644 --- a/pipelined/bevy_render2/src/render_phase/draw.rs +++ b/pipelined/bevy_render2/src/render_phase/draw.rs @@ -1,9 +1,14 @@ -use crate::render_phase::TrackedRenderPass; +use crate::{ + render_phase::TrackedRenderPass, + render_resource::{CachedPipelineId, RenderPipelineCache}, +}; use bevy_app::App; use bevy_ecs::{ all_tuples, entity::Entity, - system::{ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState}, + system::{ + lifetimeless::SRes, ReadOnlySystemParamFetch, SystemParam, SystemParamItem, SystemState, + }, world::World, }; use bevy_utils::HashMap; @@ -90,6 +95,56 @@ pub trait RenderCommand { ); } +pub trait EntityRenderCommand { + type Param: SystemParam; + fn render<'w>( + view: Entity, + item: Entity, + param: SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ); +} + +pub trait EntityPhaseItem: PhaseItem { + fn entity(&self) -> Entity; +} + +pub trait CachedPipelinePhaseItem: PhaseItem { + fn cached_pipeline(&self) -> CachedPipelineId; +} + +impl RenderCommand

for E { + type Param = E::Param; + + #[inline] + fn render<'w>( + view: Entity, + item: &P, + param: SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ) { + ::render(view, item.entity(), param, pass); + } +} + +pub struct SetItemPipeline; +impl RenderCommand

for SetItemPipeline { + type Param = SRes; + #[inline] + fn render<'w>( + _view: Entity, + item: &P, + pipeline_cache: SystemParamItem<'w, '_, Self::Param>, + pass: &mut TrackedRenderPass<'w>, + ) { + let pipeline = pipeline_cache + .into_inner() + .get_state(item.cached_pipeline()) + .unwrap(); + pass.set_render_pipeline(pipeline); + } +} + macro_rules! render_command_tuple_impl { ($($name: ident),*) => { impl),*> RenderCommand

for ($($name,)*) {