diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 85f3e93ff1be1..a0952d166650b 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -5,8 +5,8 @@ mod upsampling_pipeline; pub use settings::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}; use crate::{ - core_2d::{self, CORE_2D}, - core_3d::{self, CORE_3D}, + core_2d::graph::{Labels2d, SubGraph2d}, + core_3d::graph::{Labels3d, SubGraph3d}, }; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; @@ -72,30 +72,20 @@ impl Plugin for BloomPlugin { ), ) // Add bloom to the 3d render graph - .add_render_graph_node::>( - CORE_3D, - core_3d::graph::node::BLOOM, - ) + .add_render_graph_node::>(SubGraph3d, Labels3d::Bloom) .add_render_graph_edges( - CORE_3D, - &[ - core_3d::graph::node::END_MAIN_PASS, - core_3d::graph::node::BLOOM, - core_3d::graph::node::TONEMAPPING, - ], + SubGraph3d, + ( + Labels3d::EndMainPass, + Labels3d::Bloom, + Labels3d::Tonemapping, + ), ) // Add bloom to the 2d render graph - .add_render_graph_node::>( - CORE_2D, - core_2d::graph::node::BLOOM, - ) + .add_render_graph_node::>(SubGraph2d, Labels2d::Bloom) .add_render_graph_edges( - CORE_2D, - &[ - core_2d::graph::node::MAIN_PASS, - core_2d::graph::node::BLOOM, - core_2d::graph::node::TONEMAPPING, - ], + SubGraph2d, + (Labels2d::MainPass, Labels2d::Bloom, Labels2d::Tonemapping), ); } diff --git a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs index 6e512b873572a..94a5ae08aa047 100644 --- a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs +++ b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs @@ -1,6 +1,6 @@ use crate::{ - core_2d::{self, CORE_2D}, - core_3d::{self, CORE_3D}, + core_2d::graph::{Labels2d, SubGraph2d}, + core_3d::graph::{Labels3d, SubGraph3d}, fullscreen_vertex_shader::fullscreen_shader_vertex_state, }; use bevy_app::prelude::*; @@ -124,31 +124,37 @@ impl Plugin for CASPlugin { .add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare)); { - use core_3d::graph::node::*; render_app - .add_render_graph_node::(CORE_3D, CONTRAST_ADAPTIVE_SHARPENING) - .add_render_graph_edge(CORE_3D, TONEMAPPING, CONTRAST_ADAPTIVE_SHARPENING) + .add_render_graph_node::(SubGraph3d, Labels3d::ContrastAdaptiveSharpening) + .add_render_graph_edge( + SubGraph3d, + Labels3d::Tonemapping, + Labels3d::ContrastAdaptiveSharpening, + ) .add_render_graph_edges( - CORE_3D, - &[ - FXAA, - CONTRAST_ADAPTIVE_SHARPENING, - END_MAIN_PASS_POST_PROCESSING, - ], + SubGraph3d, + ( + Labels3d::Fxaa, + Labels3d::ContrastAdaptiveSharpening, + Labels3d::EndMainPassPostProcessing, + ), ); } { - use core_2d::graph::node::*; render_app - .add_render_graph_node::(CORE_2D, CONTRAST_ADAPTIVE_SHARPENING) - .add_render_graph_edge(CORE_2D, TONEMAPPING, CONTRAST_ADAPTIVE_SHARPENING) + .add_render_graph_node::(SubGraph2d, Labels2d::ConstrastAdaptiveSharpening) + .add_render_graph_edge( + SubGraph2d, + Labels2d::Tonemapping, + Labels2d::ConstrastAdaptiveSharpening, + ) .add_render_graph_edges( - CORE_2D, - &[ - FXAA, - CONTRAST_ADAPTIVE_SHARPENING, - END_MAIN_PASS_POST_PROCESSING, - ], + SubGraph2d, + ( + Labels2d::Fxaa, + Labels2d::ConstrastAdaptiveSharpening, + Labels2d::EndMainPassPostProcessing, + ), ); } } diff --git a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs index d76c0c315c0b6..be3c23b57dc18 100644 --- a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs +++ b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs @@ -1,4 +1,7 @@ -use crate::tonemapping::{DebandDither, Tonemapping}; +use crate::{ + core_3d::graph::SubGraph3d, + tonemapping::{DebandDither, Tonemapping}, +}; use bevy_ecs::prelude::*; use bevy_reflect::Reflect; use bevy_render::{ @@ -12,6 +15,8 @@ use bevy_render::{ }; use bevy_transform::prelude::{GlobalTransform, Transform}; +use super::graph::SubGraph2d; + #[derive(Component, Default, Reflect, Clone, ExtractComponent)] #[extract_component_filter(With)] #[reflect(Component)] @@ -49,7 +54,7 @@ impl Default for Camera2dBundle { projection.far(), ); Self { - camera_render_graph: CameraRenderGraph::new(crate::core_2d::graph::NAME), + camera_render_graph: CameraRenderGraph::new(SubGraph2d), projection, visible_entities: VisibleEntities::default(), frustum, @@ -88,7 +93,7 @@ impl Camera2dBundle { projection.far(), ); Self { - camera_render_graph: CameraRenderGraph::new(crate::core_2d::graph::NAME), + camera_render_graph: CameraRenderGraph::new(SubGraph3d), projection, visible_entities: VisibleEntities::default(), frustum, diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index f9e9fb88dcb97..bff795bcf06ad 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -2,22 +2,27 @@ mod camera_2d; mod main_pass_2d_node; pub mod graph { - pub const NAME: &str = "core_2d"; + use bevy_render::render_graph::{RenderLabel, RenderSubGraph}; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)] + pub struct SubGraph2d; + pub mod input { pub const VIEW_ENTITY: &str = "view_entity"; } - pub mod node { - pub const MSAA_WRITEBACK: &str = "msaa_writeback"; - pub const MAIN_PASS: &str = "main_pass"; - pub const BLOOM: &str = "bloom"; - pub const TONEMAPPING: &str = "tonemapping"; - pub const FXAA: &str = "fxaa"; - pub const UPSCALING: &str = "upscaling"; - pub const CONTRAST_ADAPTIVE_SHARPENING: &str = "contrast_adaptive_sharpening"; - pub const END_MAIN_PASS_POST_PROCESSING: &str = "end_main_pass_post_processing"; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + pub enum Labels2d { + MsaaWriteback, + MainPass, + Bloom, + Tonemapping, + Fxaa, + Upscaling, + ConstrastAdaptiveSharpening, + EndMainPassPostProcessing, } } -pub const CORE_2D: &str = graph::NAME; use std::ops::Range; @@ -41,6 +46,8 @@ use bevy_utils::{nonmax::NonMaxU32, FloatOrd}; use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode}; +use self::graph::{Labels2d, SubGraph2d}; + pub struct Core2dPlugin; impl Plugin for Core2dPlugin { @@ -60,21 +67,23 @@ impl Plugin for Core2dPlugin { sort_phase_system::.in_set(RenderSet::PhaseSort), ); - use graph::node::*; render_app - .add_render_sub_graph(CORE_2D) - .add_render_graph_node::(CORE_2D, MAIN_PASS) - .add_render_graph_node::>(CORE_2D, TONEMAPPING) - .add_render_graph_node::(CORE_2D, END_MAIN_PASS_POST_PROCESSING) - .add_render_graph_node::>(CORE_2D, UPSCALING) + .add_render_sub_graph(SubGraph2d) + .add_render_graph_node::(SubGraph2d, Labels2d::MainPass) + .add_render_graph_node::>( + SubGraph2d, + Labels2d::Tonemapping, + ) + .add_render_graph_node::(SubGraph2d, Labels2d::EndMainPassPostProcessing) + .add_render_graph_node::>(SubGraph2d, Labels2d::Upscaling) .add_render_graph_edges( - CORE_2D, - &[ - MAIN_PASS, - TONEMAPPING, - END_MAIN_PASS_POST_PROCESSING, - UPSCALING, - ], + SubGraph2d, + ( + Labels2d::MainPass, + Labels2d::Tonemapping, + Labels2d::EndMainPassPostProcessing, + Labels2d::Upscaling, + ), ); } } diff --git a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs index 0b8e21da0eaee..f58f148c0592c 100644 --- a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs +++ b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs @@ -11,6 +11,8 @@ use bevy_render::{ use bevy_transform::prelude::{GlobalTransform, Transform}; use serde::{Deserialize, Serialize}; +use super::graph::SubGraph3d; + /// Configuration for the "main 3d render graph". #[derive(Component, Reflect, Clone, ExtractComponent)] #[extract_component_filter(With)] @@ -150,7 +152,7 @@ pub struct Camera3dBundle { impl Default for Camera3dBundle { fn default() -> Self { Self { - camera_render_graph: CameraRenderGraph::new(crate::core_3d::graph::NAME), + camera_render_graph: CameraRenderGraph::new(SubGraph3d), camera: Default::default(), projection: Default::default(), visible_entities: Default::default(), diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 5d8f067bf3c0e..9e542d6c3240a 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -4,30 +4,36 @@ mod main_transmissive_pass_3d_node; mod main_transparent_pass_3d_node; pub mod graph { - pub const NAME: &str = "core_3d"; + use bevy_render::render_graph::{RenderLabel, RenderSubGraph}; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)] + pub struct SubGraph3d; + pub mod input { pub const VIEW_ENTITY: &str = "view_entity"; } - pub mod node { - pub const MSAA_WRITEBACK: &str = "msaa_writeback"; - pub const PREPASS: &str = "prepass"; - pub const DEFERRED_PREPASS: &str = "deferred_prepass"; - pub const COPY_DEFERRED_LIGHTING_ID: &str = "copy_deferred_lighting_id"; - pub const END_PREPASSES: &str = "end_prepasses"; - pub const START_MAIN_PASS: &str = "start_main_pass"; - pub const MAIN_OPAQUE_PASS: &str = "main_opaque_pass"; - pub const MAIN_TRANSMISSIVE_PASS: &str = "main_transmissive_pass"; - pub const MAIN_TRANSPARENT_PASS: &str = "main_transparent_pass"; - pub const END_MAIN_PASS: &str = "end_main_pass"; - pub const BLOOM: &str = "bloom"; - pub const TONEMAPPING: &str = "tonemapping"; - pub const FXAA: &str = "fxaa"; - pub const UPSCALING: &str = "upscaling"; - pub const CONTRAST_ADAPTIVE_SHARPENING: &str = "contrast_adaptive_sharpening"; - pub const END_MAIN_PASS_POST_PROCESSING: &str = "end_main_pass_post_processing"; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + pub enum Labels3d { + MsaaWriteback, + Prepass, + DeferredPrepass, + CopyDeferredLightingId, + EndPrepasses, + StartMainPass, + MainOpaquePass, + MainTransmissivePass, + MainTransparentPass, + EndMainPass, + Taa, + Bloom, + Tonemapping, + Fxaa, + Upscaling, + ContrastAdaptiveSharpening, + EndMainPassPostProcessing, } } -pub const CORE_3D: &str = graph::NAME; // PERF: vulkan docs recommend using 24 bit depth for better performance pub const CORE_3D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float; @@ -78,6 +84,8 @@ use crate::{ upscaling::UpscalingNode, }; +use self::graph::{Labels3d, SubGraph3d}; + pub struct Core3dPlugin; impl Plugin for Core3dPlugin { @@ -121,52 +129,54 @@ impl Plugin for Core3dPlugin { ), ); - use graph::node::*; render_app - .add_render_sub_graph(CORE_3D) - .add_render_graph_node::>(CORE_3D, PREPASS) + .add_render_sub_graph(SubGraph3d) + .add_render_graph_node::>(SubGraph3d, Labels3d::Prepass) .add_render_graph_node::>( - CORE_3D, - DEFERRED_PREPASS, + SubGraph3d, + Labels3d::DeferredPrepass, ) .add_render_graph_node::>( - CORE_3D, - COPY_DEFERRED_LIGHTING_ID, + SubGraph3d, + Labels3d::CopyDeferredLightingId, ) - .add_render_graph_node::(CORE_3D, END_PREPASSES) - .add_render_graph_node::(CORE_3D, START_MAIN_PASS) + .add_render_graph_node::(SubGraph3d, Labels3d::EndPrepasses) + .add_render_graph_node::(SubGraph3d, Labels3d::StartMainPass) .add_render_graph_node::>( - CORE_3D, - MAIN_OPAQUE_PASS, + SubGraph3d, + Labels3d::MainOpaquePass, ) .add_render_graph_node::>( - CORE_3D, - MAIN_TRANSMISSIVE_PASS, + SubGraph3d, + Labels3d::MainTransmissivePass, ) .add_render_graph_node::>( - CORE_3D, - MAIN_TRANSPARENT_PASS, + SubGraph3d, + Labels3d::MainTransparentPass, ) - .add_render_graph_node::(CORE_3D, END_MAIN_PASS) - .add_render_graph_node::>(CORE_3D, TONEMAPPING) - .add_render_graph_node::(CORE_3D, END_MAIN_PASS_POST_PROCESSING) - .add_render_graph_node::>(CORE_3D, UPSCALING) + .add_render_graph_node::(SubGraph3d, Labels3d::EndMainPass) + .add_render_graph_node::>( + SubGraph3d, + Labels3d::Tonemapping, + ) + .add_render_graph_node::(SubGraph3d, Labels3d::EndMainPassPostProcessing) + .add_render_graph_node::>(SubGraph3d, Labels3d::Upscaling) .add_render_graph_edges( - CORE_3D, - &[ - PREPASS, - DEFERRED_PREPASS, - COPY_DEFERRED_LIGHTING_ID, - END_PREPASSES, - START_MAIN_PASS, - MAIN_OPAQUE_PASS, - MAIN_TRANSMISSIVE_PASS, - MAIN_TRANSPARENT_PASS, - END_MAIN_PASS, - TONEMAPPING, - END_MAIN_PASS_POST_PROCESSING, - UPSCALING, - ], + SubGraph3d, + ( + Labels3d::Prepass, + Labels3d::DeferredPrepass, + Labels3d::CopyDeferredLightingId, + Labels3d::EndPrepasses, + Labels3d::StartMainPass, + Labels3d::MainOpaquePass, + Labels3d::MainTransmissivePass, + Labels3d::MainTransparentPass, + Labels3d::EndMainPass, + Labels3d::Tonemapping, + Labels3d::EndMainPassPostProcessing, + Labels3d::Upscaling, + ), ); } } diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index 26fd1bd13a013..1751b5eeffe5d 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -1,6 +1,6 @@ use crate::{ - core_2d::{self, CORE_2D}, - core_3d::{self, CORE_3D}, + core_2d::graph::{Labels2d, SubGraph2d}, + core_3d::graph::{Labels3d, SubGraph3d}, fullscreen_vertex_shader::fullscreen_shader_vertex_state, }; use bevy_app::prelude::*; @@ -95,23 +95,23 @@ impl Plugin for FxaaPlugin { render_app .init_resource::>() .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare)) - .add_render_graph_node::>(CORE_3D, core_3d::graph::node::FXAA) + .add_render_graph_node::>(SubGraph3d, Labels3d::Fxaa) .add_render_graph_edges( - CORE_3D, - &[ - core_3d::graph::node::TONEMAPPING, - core_3d::graph::node::FXAA, - core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, - ], + SubGraph3d, + ( + Labels3d::Tonemapping, + Labels3d::Fxaa, + Labels3d::EndMainPassPostProcessing, + ), ) - .add_render_graph_node::>(CORE_2D, core_2d::graph::node::FXAA) + .add_render_graph_node::>(SubGraph2d, Labels2d::Fxaa) .add_render_graph_edges( - CORE_2D, - &[ - core_2d::graph::node::TONEMAPPING, - core_2d::graph::node::FXAA, - core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING, - ], + SubGraph2d, + ( + Labels2d::Tonemapping, + Labels2d::Fxaa, + Labels2d::EndMainPassPostProcessing, + ), ); } diff --git a/crates/bevy_core_pipeline/src/msaa_writeback.rs b/crates/bevy_core_pipeline/src/msaa_writeback.rs index 06f294dae0e7f..17af382e864fb 100644 --- a/crates/bevy_core_pipeline/src/msaa_writeback.rs +++ b/crates/bevy_core_pipeline/src/msaa_writeback.rs @@ -1,7 +1,7 @@ use crate::{ blit::{BlitPipeline, BlitPipelineKey}, - core_2d::{self, CORE_2D}, - core_3d::{self, CORE_3D}, + core_2d::graph::{Labels2d, SubGraph2d}, + core_3d::graph::{Labels3d, SubGraph3d}, }; use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; @@ -31,16 +31,18 @@ impl Plugin for MsaaWritebackPlugin { prepare_msaa_writeback_pipelines.in_set(RenderSet::Prepare), ); { - use core_2d::graph::node::*; render_app - .add_render_graph_node::(CORE_2D, MSAA_WRITEBACK) - .add_render_graph_edge(CORE_2D, MSAA_WRITEBACK, MAIN_PASS); + .add_render_graph_node::(SubGraph2d, Labels2d::MsaaWriteback) + .add_render_graph_edge(SubGraph2d, Labels2d::MsaaWriteback, Labels2d::MainPass); } { - use core_3d::graph::node::*; render_app - .add_render_graph_node::(CORE_3D, MSAA_WRITEBACK) - .add_render_graph_edge(CORE_3D, MSAA_WRITEBACK, START_MAIN_PASS); + .add_render_graph_node::(SubGraph3d, Labels3d::MsaaWriteback) + .add_render_graph_edge( + SubGraph3d, + Labels3d::MsaaWriteback, + Labels3d::StartMainPass, + ); } } } diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index b0eb4f3057e69..3cbfcc2d238ab 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -1,5 +1,5 @@ use crate::{ - core_3d::{self, CORE_3D}, + core_3d::graph::{Labels3d, SubGraph3d}, fullscreen_vertex_shader::fullscreen_shader_vertex_state, prelude::Camera3d, prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures}, @@ -35,13 +35,6 @@ use bevy_render::{ ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, }; -pub mod draw_3d_graph { - pub mod node { - /// Label for the TAA render node. - pub const TAA: &str = "taa"; - } -} - const TAA_SHADER_HANDLE: Handle = Handle::weak_from_u128(656865235226276); /// Plugin for temporal anti-aliasing. Disables multisample anti-aliasing (MSAA). @@ -72,17 +65,17 @@ impl Plugin for TemporalAntiAliasPlugin { ), ) .add_render_graph_node::>( - CORE_3D, - draw_3d_graph::node::TAA, + SubGraph3d, + Labels3d::Taa, ) .add_render_graph_edges( - CORE_3D, - &[ - core_3d::graph::node::END_MAIN_PASS, - draw_3d_graph::node::TAA, - core_3d::graph::node::BLOOM, - core_3d::graph::node::TONEMAPPING, - ], + SubGraph3d, + ( + Labels3d::EndMainPass, + Labels3d::Taa, + Labels3d::Bloom, + Labels3d::Tonemapping, + ), ); } diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index d7d76b355b518..67aed41b6a80c 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -1,11 +1,11 @@ use crate::{ - environment_map::RenderViewEnvironmentMaps, MeshPipeline, MeshViewBindGroup, + environment_map::RenderViewEnvironmentMaps, graph::LabelsPbr, MeshPipeline, MeshViewBindGroup, ScreenSpaceAmbientOcclusionSettings, ViewLightProbesUniformOffset, }; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, Handle}; use bevy_core_pipeline::{ - core_3d, + core_3d::graph::{Labels3d, SubGraph3d}, deferred::{ copy_lighting_id::DeferredLightingIdDepthTexture, DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT, }, @@ -115,16 +115,16 @@ impl Plugin for DeferredPbrLightingPlugin { (prepare_deferred_lighting_pipelines.in_set(RenderSet::Prepare),), ) .add_render_graph_node::>( - core_3d::graph::NAME, - DEFERRED_LIGHTING_PASS, + SubGraph3d, + LabelsPbr::DeferredLightingPass, ) .add_render_graph_edges( - core_3d::graph::NAME, - &[ - core_3d::graph::node::START_MAIN_PASS, - DEFERRED_LIGHTING_PASS, - core_3d::graph::node::MAIN_OPAQUE_PASS, - ], + SubGraph3d, + ( + Labels3d::StartMainPass, + LabelsPbr::DeferredLightingPass, + Labels3d::MainOpaquePass, + ), ); } @@ -137,7 +137,6 @@ impl Plugin for DeferredPbrLightingPlugin { } } -pub const DEFERRED_LIGHTING_PASS: &str = "deferred_opaque_pbr_lighting_pass_3d"; #[derive(Default)] pub struct DeferredOpaquePass3dPbrLightingNode; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 97fd0a0ecc5bd..c1556c8d360d1 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -16,6 +16,7 @@ mod render; mod ssao; pub use alpha::*; +use bevy_core_pipeline::core_3d::graph::{Labels3d, SubGraph3d}; pub use bundle::*; pub use extended_material::*; pub use fog::*; @@ -50,10 +51,16 @@ pub mod prelude { }; } -pub mod draw_3d_graph { - pub mod node { +pub mod graph { + use bevy_render::render_graph::RenderLabel; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + pub enum LabelsPbr { /// Label for the shadow pass node. - pub const SHADOW_PASS: &str = "shadow_pass"; + ShadowPass, + /// Label for the screen space ambient occlusion render node. + ScreenSpaceAmbientOcclusion, + DeferredLightingPass, } } @@ -75,7 +82,7 @@ use bevy_render::{ }; use bevy_transform::TransformSystem; -use crate::deferred::DeferredPbrLightingPlugin; +use crate::{deferred::DeferredPbrLightingPlugin, graph::LabelsPbr}; pub const PBR_TYPES_SHADER_HANDLE: Handle = Handle::weak_from_u128(1708015359337029744); pub const PBR_BINDINGS_SHADER_HANDLE: Handle = Handle::weak_from_u128(5635987986427308186); @@ -353,14 +360,9 @@ impl Plugin for PbrPlugin { let shadow_pass_node = ShadowPassNode::new(&mut render_app.world); let mut graph = render_app.world.resource_mut::(); - let draw_3d_graph = graph - .get_sub_graph_mut(bevy_core_pipeline::core_3d::graph::NAME) - .unwrap(); - draw_3d_graph.add_node(draw_3d_graph::node::SHADOW_PASS, shadow_pass_node); - draw_3d_graph.add_node_edge( - draw_3d_graph::node::SHADOW_PASS, - bevy_core_pipeline::core_3d::graph::node::START_MAIN_PASS, - ); + let draw_3d_graph = graph.get_sub_graph_mut(SubGraph3d).unwrap(); + draw_3d_graph.add_node(LabelsPbr::ShadowPass, shadow_pass_node); + draw_3d_graph.add_node_edge(LabelsPbr::ShadowPass, Labels3d::StartMainPass); render_app.ignore_ambiguity( bevy_render::Render, diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index d9c9b8dc0fea1..30054f5cd184b 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -1,7 +1,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; use bevy_core_pipeline::{ - core_3d::CORE_3D, + core_3d::graph::{Labels3d, SubGraph3d}, prelude::Camera3d, prepass::{DepthPrepass, NormalPrepass, ViewPrepassTextures}, }; @@ -37,12 +37,7 @@ use bevy_utils::{ }; use std::mem; -pub mod draw_3d_graph { - pub mod node { - /// Label for the screen space ambient occlusion render node. - pub const SCREEN_SPACE_AMBIENT_OCCLUSION: &str = "screen_space_ambient_occlusion"; - } -} +use crate::LabelsPbr; const PREPROCESS_DEPTH_SHADER_HANDLE: Handle = Handle::weak_from_u128(102258915420479); const GTAO_SHADER_HANDLE: Handle = Handle::weak_from_u128(253938746510568); @@ -117,17 +112,17 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { ), ) .add_render_graph_node::>( - CORE_3D, - draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION, + SubGraph3d, + LabelsPbr::ScreenSpaceAmbientOcclusion, ) .add_render_graph_edges( - CORE_3D, - &[ + SubGraph3d, + ( // END_PRE_PASSES -> SCREEN_SPACE_AMBIENT_OCCLUSION -> MAIN_PASS - bevy_core_pipeline::core_3d::graph::node::END_PREPASSES, - draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION, - bevy_core_pipeline::core_3d::graph::node::START_MAIN_PASS, - ], + Labels3d::EndPrepasses, + LabelsPbr::ScreenSpaceAmbientOcclusion, + Labels3d::StartMainPass, + ), ); } } diff --git a/crates/bevy_render/macros/src/lib.rs b/crates/bevy_render/macros/src/lib.rs index 97126ba830bf4..c0d04e6c776a3 100644 --- a/crates/bevy_render/macros/src/lib.rs +++ b/crates/bevy_render/macros/src/lib.rs @@ -2,8 +2,9 @@ mod as_bind_group; mod extract_component; mod extract_resource; -use bevy_macro_utils::BevyManifest; +use bevy_macro_utils::{derive_label, BevyManifest}; use proc_macro::TokenStream; +use quote::format_ident; use syn::{parse_macro_input, DeriveInput}; pub(crate) fn bevy_render_path() -> syn::Path { @@ -58,3 +59,39 @@ pub fn derive_as_bind_group(input: TokenStream) -> TokenStream { as_bind_group::derive_as_bind_group(input).unwrap_or_else(|err| err.to_compile_error().into()) } + +/// Derive macro generating an impl of the trait `RenderLabel`. +/// +/// This does not work for unions. +#[proc_macro_derive(RenderLabel)] +pub fn derive_render_label(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let mut trait_path = bevy_render_path(); + trait_path + .segments + .push(format_ident!("render_graph").into()); + let mut dyn_eq_path = trait_path.clone(); + trait_path + .segments + .push(format_ident!("RenderLabel").into()); + dyn_eq_path.segments.push(format_ident!("DynEq").into()); + derive_label(input, "RenderLabel", &trait_path, &dyn_eq_path) +} + +/// Derive macro generating an impl of the trait `RenderSubGraph`. +/// +/// This does not work for unions. +#[proc_macro_derive(RenderSubGraph)] +pub fn derive_render_sub_graph(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let mut trait_path = bevy_render_path(); + trait_path + .segments + .push(format_ident!("render_graph").into()); + let mut dyn_eq_path = trait_path.clone(); + trait_path + .segments + .push(format_ident!("RenderSubGraph").into()); + dyn_eq_path.segments.push(format_ident!("DynEq").into()); + derive_label(input, "RenderSubGraph", &trait_path, &dyn_eq_path) +} diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 540f0fc3e30f5..a2fff3edf0468 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -4,6 +4,7 @@ use crate::{ prelude::Image, primitives::Frustum, render_asset::RenderAssets, + render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, view::{ColorGrading, ExtractedView, ExtractedWindows, RenderLayers, VisibleEntities}, Extract, @@ -31,7 +32,7 @@ use bevy_window::{ NormalizedWindowRef, PrimaryWindow, Window, WindowCreated, WindowRef, WindowResized, WindowScaleFactorChanged, }; -use std::{borrow::Cow, ops::Range}; +use std::ops::Range; use wgpu::{BlendState, LoadOp, TextureFormat, TextureUsages}; use super::{ClearColorConfig, Projection}; @@ -476,21 +477,20 @@ impl Default for CameraOutputMode { } /// Configures the [`RenderGraph`](crate::render_graph::RenderGraph) name assigned to be run for a given [`Camera`] entity. -#[derive(Component, Deref, DerefMut, Reflect, Default)] -#[reflect(Component)] -pub struct CameraRenderGraph(Cow<'static, str>); +#[derive(Component, Deref, DerefMut)] +pub struct CameraRenderGraph(InternedRenderSubGraph); impl CameraRenderGraph { /// Creates a new [`CameraRenderGraph`] from any string-like type. #[inline] - pub fn new>>(name: T) -> Self { - Self(name.into()) + pub fn new(name: T) -> Self { + Self(name.intern()) } /// Sets the graph name. #[inline] - pub fn set>>(&mut self, name: T) { - self.0 = name.into(); + pub fn set(&mut self, name: T) { + self.0 = name.intern(); } } @@ -770,7 +770,7 @@ pub struct ExtractedCamera { pub physical_viewport_size: Option, pub physical_target_size: Option, pub viewport: Option, - pub render_graph: Cow<'static, str>, + pub render_graph: InternedRenderSubGraph, pub order: isize, pub output_mode: CameraOutputMode, pub msaa_writeback: bool, @@ -843,7 +843,7 @@ pub fn extract_cameras( viewport: camera.viewport.clone(), physical_viewport_size: Some(viewport_size), physical_target_size: Some(target_size), - render_graph: camera_render_graph.0.clone(), + render_graph: camera_render_graph.0, order: camera.order, output_mode: camera.output_mode, msaa_writeback: camera.msaa_writeback, diff --git a/crates/bevy_render/src/camera/camera_driver_node.rs b/crates/bevy_render/src/camera/camera_driver_node.rs index 9f24b72fde862..5845f216bb1bc 100644 --- a/crates/bevy_render/src/camera/camera_driver_node.rs +++ b/crates/bevy_render/src/camera/camera_driver_node.rs @@ -49,11 +49,7 @@ impl Node for CameraDriverNode { } } if run_graph { - graph.run_sub_graph( - camera.render_graph.clone(), - vec![], - Some(sorted_camera.entity), - )?; + graph.run_sub_graph(camera.render_graph, vec![], Some(sorted_camera.entity))?; } } diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 214ced79d25fc..09961a28a7f6e 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -27,7 +27,6 @@ impl Plugin for CameraPlugin { .register_type::() .register_type::>() .register_type::() - .register_type::() .register_type::() .register_type::() .register_type::() @@ -49,7 +48,7 @@ impl Plugin for CameraPlugin { .add_systems(Render, sort_cameras.in_set(RenderSet::ManageViews)); let camera_driver_node = CameraDriverNode::new(&mut render_app.world); let mut render_graph = render_app.world.resource_mut::(); - render_graph.add_node(crate::main_graph::node::CAMERA_DRIVER, camera_driver_node); + render_graph.add_node(crate::graph::CameraDriverLabel, camera_driver_node); } } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 5014b948537bc..ce0e11e842cfe 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -187,10 +187,11 @@ impl DerefMut for MainWorld { } } -pub mod main_graph { - pub mod node { - pub const CAMERA_DRIVER: &str = "camera_driver"; - } +pub mod graph { + use crate::render_graph::RenderLabel; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + pub struct CameraDriverLabel; } #[derive(Resource)] diff --git a/crates/bevy_render/src/render_graph/app.rs b/crates/bevy_render/src/render_graph/app.rs index 3d301348a339e..97ababa312b73 100644 --- a/crates/bevy_render/src/render_graph/app.rs +++ b/crates/bevy_render/src/render_graph/app.rs @@ -2,90 +2,100 @@ use bevy_app::App; use bevy_ecs::world::FromWorld; use bevy_log::warn; -use super::{Node, RenderGraph}; +use super::{IntoRenderNodeArray, Node, RenderGraph, RenderLabel, RenderSubGraph}; /// Adds common [`RenderGraph`] operations to [`App`]. pub trait RenderGraphApp { // Add a sub graph to the [`RenderGraph`] - fn add_render_sub_graph(&mut self, sub_graph_name: &'static str) -> &mut Self; + fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self; /// Add a [`Node`] to the [`RenderGraph`]: /// * Create the [`Node`] using the [`FromWorld`] implementation /// * Add it to the graph fn add_render_graph_node( &mut self, - sub_graph_name: &'static str, - node_name: &'static str, + sub_graph: impl RenderSubGraph, + node_label: impl RenderLabel, ) -> &mut Self; /// Automatically add the required node edges based on the given ordering - fn add_render_graph_edges( + fn add_render_graph_edges( &mut self, - sub_graph_name: &'static str, - edges: &[&'static str], + sub_graph: impl RenderSubGraph, + edges: impl IntoRenderNodeArray, ) -> &mut Self; + /// Add node edge to the specified graph fn add_render_graph_edge( &mut self, - sub_graph_name: &'static str, - output_edge: &'static str, - input_edge: &'static str, + sub_graph: impl RenderSubGraph, + output_node: impl RenderLabel, + input_node: impl RenderLabel, ) -> &mut Self; } impl RenderGraphApp for App { - fn add_render_sub_graph(&mut self, sub_graph_name: &'static str) -> &mut Self { + fn add_render_sub_graph(&mut self, sub_graph: impl RenderSubGraph) -> &mut Self { let mut render_graph = self.world.get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_sub_graph on the RenderApp", ); - render_graph.add_sub_graph(sub_graph_name, RenderGraph::default()); + render_graph.add_sub_graph(sub_graph, RenderGraph::default()); self } fn add_render_graph_node( &mut self, - sub_graph_name: &'static str, - node_name: &'static str, + sub_graph: impl RenderSubGraph, + node_label: impl RenderLabel, ) -> &mut Self { + let sub_graph = sub_graph.intern(); let node = T::from_world(&mut self.world); let mut render_graph = self.world.get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp", ); - if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph_name) { - graph.add_node(node_name, node); + if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { + graph.add_node(node_label, node); } else { - warn!("Tried adding a render graph node to {sub_graph_name} but the sub graph doesn't exist"); + warn!( + "Tried adding a render graph node to {sub_graph:?} but the sub graph doesn't exist" + ); } self } - fn add_render_graph_edges( + fn add_render_graph_edges( &mut self, - sub_graph_name: &'static str, - edges: &[&'static str], + sub_graph: impl RenderSubGraph, + edges: impl IntoRenderNodeArray, ) -> &mut Self { + let sub_graph = sub_graph.intern(); let mut render_graph = self.world.get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_edges on the RenderApp", ); - if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph_name) { + if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { graph.add_node_edges(edges); } else { - warn!("Tried adding render graph edges to {sub_graph_name} but the sub graph doesn't exist"); + warn!( + "Tried adding render graph edges to {sub_graph:?} but the sub graph doesn't exist" + ); } self } fn add_render_graph_edge( &mut self, - sub_graph_name: &'static str, - output_edge: &'static str, - input_edge: &'static str, + sub_graph: impl RenderSubGraph, + output_node: impl RenderLabel, + input_node: impl RenderLabel, ) -> &mut Self { + let sub_graph = sub_graph.intern(); let mut render_graph = self.world.get_resource_mut::().expect( "RenderGraph not found. Make sure you are using add_render_graph_edge on the RenderApp", ); - if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph_name) { - graph.add_node_edge(output_edge, input_edge); + if let Some(graph) = render_graph.get_sub_graph_mut(sub_graph) { + graph.add_node_edge(output_node, input_node); } else { - warn!("Tried adding a render graph edge to {sub_graph_name} but the sub graph doesn't exist"); + warn!( + "Tried adding a render graph edge to {sub_graph:?} but the sub graph doesn't exist" + ); } self } diff --git a/crates/bevy_render/src/render_graph/context.rs b/crates/bevy_render/src/render_graph/context.rs index da983fdc7fbae..e62917465d6b5 100644 --- a/crates/bevy_render/src/render_graph/context.rs +++ b/crates/bevy_render/src/render_graph/context.rs @@ -6,10 +6,12 @@ use bevy_ecs::entity::Entity; use std::borrow::Cow; use thiserror::Error; -/// A command that signals the graph runner to run the sub graph corresponding to the `name` +use super::{InternedRenderSubGraph, RenderSubGraph}; + +/// A command that signals the graph runner to run the sub graph corresponding to the `sub_graph` /// with the specified `inputs` next. pub struct RunSubGraph { - pub name: Cow<'static, str>, + pub sub_graph: InternedRenderSubGraph, pub inputs: Vec, pub view_entity: Option, } @@ -180,15 +182,15 @@ impl<'a> RenderGraphContext<'a> { /// Queues up a sub graph for execution after the node has finished running. pub fn run_sub_graph( &mut self, - name: impl Into>, + name: impl RenderSubGraph, inputs: Vec, view_entity: Option, ) -> Result<(), RunSubGraphError> { - let name = name.into(); + let name = name.intern(); let sub_graph = self .graph - .get_sub_graph(&name) - .ok_or_else(|| RunSubGraphError::MissingSubGraph(name.clone()))?; + .get_sub_graph(name) + .ok_or(RunSubGraphError::MissingSubGraph(name))?; if let Some(input_node) = sub_graph.get_input_node() { for (i, input_slot) in input_node.input_slots.iter().enumerate() { if let Some(input_value) = inputs.get(i) { @@ -214,7 +216,7 @@ impl<'a> RenderGraphContext<'a> { } self.run_sub_graphs.push(RunSubGraph { - name, + sub_graph: name, inputs, view_entity, }); @@ -231,19 +233,19 @@ impl<'a> RenderGraphContext<'a> { #[derive(Error, Debug, Eq, PartialEq)] pub enum RunSubGraphError { - #[error("attempted to run sub-graph `{0}`, but it does not exist")] - MissingSubGraph(Cow<'static, str>), - #[error("attempted to pass inputs to sub-graph `{0}`, which has no input slots")] - SubGraphHasNoInputs(Cow<'static, str>), + #[error("attempted to run sub-graph `{0:?}`, but it does not exist")] + MissingSubGraph(InternedRenderSubGraph), + #[error("attempted to pass inputs to sub-graph `{0:?}`, which has no input slots")] + SubGraphHasNoInputs(InternedRenderSubGraph), #[error("sub graph (name: `{graph_name:?}`) could not be run because slot `{slot_name}` at index {slot_index} has no value")] MissingInput { slot_index: usize, slot_name: Cow<'static, str>, - graph_name: Cow<'static, str>, + graph_name: InternedRenderSubGraph, }, #[error("attempted to use the wrong type for input slot")] MismatchedInputSlotType { - graph_name: Cow<'static, str>, + graph_name: InternedRenderSubGraph, slot_index: usize, label: SlotLabel, expected: SlotType, diff --git a/crates/bevy_render/src/render_graph/edge.rs b/crates/bevy_render/src/render_graph/edge.rs index 4ba85fca57892..fa9943bb4dd02 100644 --- a/crates/bevy_render/src/render_graph/edge.rs +++ b/crates/bevy_render/src/render_graph/edge.rs @@ -1,4 +1,4 @@ -use super::NodeId; +use super::InternedRenderLabel; /// An edge, which connects two [`Nodes`](super::Node) in /// a [`RenderGraph`](crate::render_graph::RenderGraph). @@ -22,28 +22,28 @@ pub enum Edge { /// and connecting the output slot at the `output_index` of the output_node /// with the slot at the `input_index` of the `input_node`. SlotEdge { - input_node: NodeId, + input_node: InternedRenderLabel, input_index: usize, - output_node: NodeId, + output_node: InternedRenderLabel, output_index: usize, }, /// An edge describing to ordering of both nodes (`output_node` before `input_node`). NodeEdge { - input_node: NodeId, - output_node: NodeId, + input_node: InternedRenderLabel, + output_node: InternedRenderLabel, }, } impl Edge { /// Returns the id of the `input_node`. - pub fn get_input_node(&self) -> NodeId { + pub fn get_input_node(&self) -> InternedRenderLabel { match self { Edge::SlotEdge { input_node, .. } | Edge::NodeEdge { input_node, .. } => *input_node, } } /// Returns the id of the `output_node`. - pub fn get_output_node(&self) -> NodeId { + pub fn get_output_node(&self) -> InternedRenderLabel { match self { Edge::SlotEdge { output_node, .. } | Edge::NodeEdge { output_node, .. } => *output_node, } diff --git a/crates/bevy_render/src/render_graph/graph.rs b/crates/bevy_render/src/render_graph/graph.rs index 0263ede658368..e62489ded6b81 100644 --- a/crates/bevy_render/src/render_graph/graph.rs +++ b/crates/bevy_render/src/render_graph/graph.rs @@ -1,15 +1,26 @@ use crate::{ render_graph::{ - Edge, Node, NodeId, NodeLabel, NodeRunError, NodeState, RenderGraphContext, - RenderGraphError, SlotInfo, SlotLabel, + Edge, Node, NodeRunError, NodeState, RenderGraphContext, RenderGraphError, RenderLabel, + SlotInfo, SlotLabel, }, renderer::RenderContext, }; use bevy_ecs::{prelude::World, system::Resource}; -use bevy_utils::HashMap; -use std::{borrow::Cow, fmt::Debug}; +use bevy_utils::{define_label, intern::Interned, HashMap}; +use std::fmt::Debug; -use super::EdgeExistence; +use super::{EdgeExistence, InternedRenderLabel, IntoRenderNodeArray}; + +pub use bevy_render_macros::RenderSubGraph; + +define_label!( + /// A strongly-typed class of labels used to identify a [`SubGraph`] in a render graph. + RenderSubGraph, + RENDER_SUB_GRAPH_INTERNER +); + +/// A shorthand for `Interned`. +pub type InternedRenderSubGraph = Interned; /// The render graph configures the modular, parallel and re-usable render logic. /// It is a retained and stateless (nodes themselves may have their own internal state) structure, @@ -29,12 +40,19 @@ use super::EdgeExistence; /// /// ## Example /// Here is a simple render graph example with two nodes connected by a node edge. -/// ``` +/// ```ignore +/// # TODO: Remove when #10645 is fixed /// # use bevy_app::prelude::*; /// # use bevy_ecs::prelude::World; -/// # use bevy_render::render_graph::{RenderGraph, Node, RenderGraphContext, NodeRunError}; +/// # use bevy_render::render_graph::{RenderGraph, RenderLabel, Node, RenderGraphContext, NodeRunError}; /// # use bevy_render::renderer::RenderContext; /// # +/// #[derive(RenderLabel)] +/// enum Labels { +/// A, +/// B, +/// } +/// /// # struct MyNode; /// # /// # impl Node for MyNode { @@ -44,22 +62,21 @@ use super::EdgeExistence; /// # } /// # /// let mut graph = RenderGraph::default(); -/// graph.add_node("input_node", MyNode); -/// graph.add_node("output_node", MyNode); -/// graph.add_node_edge("output_node", "input_node"); +/// graph.add_node(Labels::A, MyNode); +/// graph.add_node(Labels::B, MyNode); +/// graph.add_node_edge(Labels::B, Labels::A); /// ``` #[derive(Resource, Default)] pub struct RenderGraph { - nodes: HashMap, - node_names: HashMap, NodeId>, - sub_graphs: HashMap, RenderGraph>, - input_node: Option, + nodes: HashMap, + sub_graphs: HashMap, } -impl RenderGraph { - /// The name of the [`GraphInputNode`] of this graph. Used to connect other nodes to it. - pub const INPUT_NODE_NAME: &'static str = "GraphInputNode"; +/// The label for the input node of a graph. Used to connect other nodes to it. +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +pub struct GraphInput; +impl RenderGraph { /// Updates all nodes and sub graphs of the render graph. Should be called before executing it. pub fn update(&mut self, world: &mut World) { for node in self.nodes.values_mut() { @@ -72,12 +89,16 @@ impl RenderGraph { } /// Creates an [`GraphInputNode`] with the specified slots if not already present. - pub fn set_input(&mut self, inputs: Vec) -> NodeId { - assert!(self.input_node.is_none(), "Graph already has an input node"); + pub fn set_input(&mut self, inputs: Vec) { + assert!( + matches!( + self.get_node_state(GraphInput), + Err(RenderGraphError::InvalidNode(_)) + ), + "Graph already has an input node" + ); - let id = self.add_node("GraphInputNode", GraphInputNode { inputs }); - self.input_node = Some(id); - id + self.add_node(GraphInput, GraphInputNode { inputs }); } /// Returns the [`NodeState`] of the input node of this graph. @@ -87,7 +108,7 @@ impl RenderGraph { /// - [`input_node`](Self::input_node) for an unchecked version. #[inline] pub fn get_input_node(&self) -> Option<&NodeState> { - self.input_node.and_then(|id| self.get_node_state(id).ok()) + self.get_node_state(GraphInput).ok() } /// Returns the [`NodeState`] of the input node of this graph. @@ -104,27 +125,23 @@ impl RenderGraph { self.get_input_node().unwrap() } - /// Adds the `node` with the `name` to the graph. - /// If the name is already present replaces it instead. - pub fn add_node(&mut self, name: impl Into>, node: T) -> NodeId + /// Adds the `node` with the `label` to the graph. + /// If the label is already present replaces it instead. + pub fn add_node(&mut self, label: impl RenderLabel, node: T) where T: Node, { - let id = NodeId::new(); - let name = name.into(); - let mut node_state = NodeState::new(id, node); - node_state.name = Some(name.clone()); - self.nodes.insert(id, node_state); - self.node_names.insert(name, id); - id + let label = label.intern(); + let node_state = NodeState::new(label, node); + self.nodes.insert(label, node_state); } /// Add `node_edge`s based on the order of the given `edges` array. /// /// Defining an edge that already exists is not considered an error with this api. /// It simply won't create a new edge. - pub fn add_node_edges(&mut self, edges: &[&'static str]) { - for window in edges.windows(2) { + pub fn add_node_edges(&mut self, edges: impl IntoRenderNodeArray) { + for window in edges.into_array().windows(2) { let [a, b] = window else { break; }; @@ -139,47 +156,42 @@ impl RenderGraph { } } - /// Removes the `node` with the `name` from the graph. - /// If the name is does not exist, nothing happens. - pub fn remove_node( - &mut self, - name: impl Into>, - ) -> Result<(), RenderGraphError> { - let name = name.into(); - if let Some(id) = self.node_names.remove(&name) { - if let Some(node_state) = self.nodes.remove(&id) { - // Remove all edges from other nodes to this one. Note that as we're removing this - // node, we don't need to remove its input edges - for input_edge in node_state.edges.input_edges() { - match input_edge { - Edge::SlotEdge { output_node, .. } - | Edge::NodeEdge { - input_node: _, - output_node, - } => { - if let Ok(output_node) = self.get_node_state_mut(*output_node) { - output_node.edges.remove_output_edge(input_edge.clone())?; - } + /// Removes the `node` with the `label` from the graph. + /// If the label does not exist, nothing happens. + pub fn remove_node(&mut self, label: impl RenderLabel) -> Result<(), RenderGraphError> { + let label = label.intern(); + if let Some(node_state) = self.nodes.remove(&label) { + // Remove all edges from other nodes to this one. Note that as we're removing this + // node, we don't need to remove its input edges + for input_edge in node_state.edges.input_edges() { + match input_edge { + Edge::SlotEdge { output_node, .. } + | Edge::NodeEdge { + input_node: _, + output_node, + } => { + if let Ok(output_node) = self.get_node_state_mut(*output_node) { + output_node.edges.remove_output_edge(input_edge.clone())?; } } } - // Remove all edges from this node to other nodes. Note that as we're removing this - // node, we don't need to remove its output edges - for output_edge in node_state.edges.output_edges() { - match output_edge { - Edge::SlotEdge { - output_node: _, - output_index: _, - input_node, - input_index: _, - } - | Edge::NodeEdge { - output_node: _, - input_node, - } => { - if let Ok(input_node) = self.get_node_state_mut(*input_node) { - input_node.edges.remove_input_edge(output_edge.clone())?; - } + } + // Remove all edges from this node to other nodes. Note that as we're removing this + // node, we don't need to remove its output edges + for output_edge in node_state.edges.output_edges() { + match output_edge { + Edge::SlotEdge { + output_node: _, + output_index: _, + input_node, + input_index: _, + } + | Edge::NodeEdge { + output_node: _, + input_node, + } => { + if let Ok(input_node) = self.get_node_state_mut(*input_node) { + input_node.edges.remove_input_edge(output_edge.clone())?; } } } @@ -190,44 +202,26 @@ impl RenderGraph { } /// Retrieves the [`NodeState`] referenced by the `label`. - pub fn get_node_state( - &self, - label: impl Into, - ) -> Result<&NodeState, RenderGraphError> { - let label = label.into(); - let node_id = self.get_node_id(&label)?; + pub fn get_node_state(&self, label: impl RenderLabel) -> Result<&NodeState, RenderGraphError> { + let label = label.intern(); self.nodes - .get(&node_id) + .get(&label) .ok_or(RenderGraphError::InvalidNode(label)) } /// Retrieves the [`NodeState`] referenced by the `label` mutably. pub fn get_node_state_mut( &mut self, - label: impl Into, + label: impl RenderLabel, ) -> Result<&mut NodeState, RenderGraphError> { - let label = label.into(); - let node_id = self.get_node_id(&label)?; + let label = label.intern(); self.nodes - .get_mut(&node_id) + .get_mut(&label) .ok_or(RenderGraphError::InvalidNode(label)) } - /// Retrieves the [`NodeId`] referenced by the `label`. - pub fn get_node_id(&self, label: impl Into) -> Result { - let label = label.into(); - match label { - NodeLabel::Id(id) => Ok(id), - NodeLabel::Name(ref name) => self - .node_names - .get(name) - .cloned() - .ok_or(RenderGraphError::InvalidNode(label)), - } - } - /// Retrieves the [`Node`] referenced by the `label`. - pub fn get_node(&self, label: impl Into) -> Result<&T, RenderGraphError> + pub fn get_node(&self, label: impl RenderLabel) -> Result<&T, RenderGraphError> where T: Node, { @@ -235,10 +229,7 @@ impl RenderGraph { } /// Retrieves the [`Node`] referenced by the `label` mutably. - pub fn get_node_mut( - &mut self, - label: impl Into, - ) -> Result<&mut T, RenderGraphError> + pub fn get_node_mut(&mut self, label: impl RenderLabel) -> Result<&mut T, RenderGraphError> where T: Node, { @@ -248,48 +239,49 @@ impl RenderGraph { /// Adds the [`Edge::SlotEdge`] to the graph. This guarantees that the `output_node` /// is run before the `input_node` and also connects the `output_slot` to the `input_slot`. /// - /// Fails if any invalid [`NodeLabel`]s or [`SlotLabel`]s are given. + /// Fails if any invalid [`RenderLabel`]s or [`SlotLabel`]s are given. /// /// # See also /// /// - [`add_slot_edge`](Self::add_slot_edge) for an infallible version. pub fn try_add_slot_edge( &mut self, - output_node: impl Into, + output_node: impl RenderLabel, output_slot: impl Into, - input_node: impl Into, + input_node: impl RenderLabel, input_slot: impl Into, ) -> Result<(), RenderGraphError> { let output_slot = output_slot.into(); let input_slot = input_slot.into(); - let output_node_id = self.get_node_id(output_node)?; - let input_node_id = self.get_node_id(input_node)?; + + let output_node = output_node.intern(); + let input_node = input_node.intern(); let output_index = self - .get_node_state(output_node_id)? + .get_node_state(output_node)? .output_slots .get_slot_index(output_slot.clone()) .ok_or(RenderGraphError::InvalidOutputNodeSlot(output_slot))?; let input_index = self - .get_node_state(input_node_id)? + .get_node_state(input_node)? .input_slots .get_slot_index(input_slot.clone()) .ok_or(RenderGraphError::InvalidInputNodeSlot(input_slot))?; let edge = Edge::SlotEdge { - output_node: output_node_id, + output_node, output_index, - input_node: input_node_id, + input_node, input_index, }; self.validate_edge(&edge, EdgeExistence::DoesNotExist)?; { - let output_node = self.get_node_state_mut(output_node_id)?; + let output_node = self.get_node_state_mut(output_node)?; output_node.edges.add_output_edge(edge.clone())?; } - let input_node = self.get_node_state_mut(input_node_id)?; + let input_node = self.get_node_state_mut(input_node)?; input_node.edges.add_input_edge(edge)?; Ok(()) @@ -300,16 +292,16 @@ impl RenderGraph { /// /// # Panics /// - /// Any invalid [`NodeLabel`]s or [`SlotLabel`]s are given. + /// Any invalid [`RenderLabel`]s or [`SlotLabel`]s are given. /// /// # See also /// /// - [`try_add_slot_edge`](Self::try_add_slot_edge) for a fallible version. pub fn add_slot_edge( &mut self, - output_node: impl Into, + output_node: impl RenderLabel, output_slot: impl Into, - input_node: impl Into, + input_node: impl RenderLabel, input_slot: impl Into, ) { self.try_add_slot_edge(output_node, output_slot, input_node, input_slot) @@ -320,41 +312,42 @@ impl RenderGraph { /// nothing happens. pub fn remove_slot_edge( &mut self, - output_node: impl Into, + output_node: impl RenderLabel, output_slot: impl Into, - input_node: impl Into, + input_node: impl RenderLabel, input_slot: impl Into, ) -> Result<(), RenderGraphError> { let output_slot = output_slot.into(); let input_slot = input_slot.into(); - let output_node_id = self.get_node_id(output_node)?; - let input_node_id = self.get_node_id(input_node)?; + + let output_node = output_node.intern(); + let input_node = input_node.intern(); let output_index = self - .get_node_state(output_node_id)? + .get_node_state(output_node)? .output_slots .get_slot_index(output_slot.clone()) .ok_or(RenderGraphError::InvalidOutputNodeSlot(output_slot))?; let input_index = self - .get_node_state(input_node_id)? + .get_node_state(input_node)? .input_slots .get_slot_index(input_slot.clone()) .ok_or(RenderGraphError::InvalidInputNodeSlot(input_slot))?; let edge = Edge::SlotEdge { - output_node: output_node_id, + output_node, output_index, - input_node: input_node_id, + input_node, input_index, }; self.validate_edge(&edge, EdgeExistence::Exists)?; { - let output_node = self.get_node_state_mut(output_node_id)?; + let output_node = self.get_node_state_mut(output_node)?; output_node.edges.remove_output_edge(edge.clone())?; } - let input_node = self.get_node_state_mut(input_node_id)?; + let input_node = self.get_node_state_mut(input_node)?; input_node.edges.remove_input_edge(edge)?; Ok(()) @@ -363,31 +356,31 @@ impl RenderGraph { /// Adds the [`Edge::NodeEdge`] to the graph. This guarantees that the `output_node` /// is run before the `input_node`. /// - /// Fails if any invalid [`NodeLabel`] is given. + /// Fails if any invalid [`RenderLabel`] is given. /// /// # See also /// /// - [`add_node_edge`](Self::add_node_edge) for an infallible version. pub fn try_add_node_edge( &mut self, - output_node: impl Into, - input_node: impl Into, + output_node: impl RenderLabel, + input_node: impl RenderLabel, ) -> Result<(), RenderGraphError> { - let output_node_id = self.get_node_id(output_node)?; - let input_node_id = self.get_node_id(input_node)?; + let output_node = output_node.intern(); + let input_node = input_node.intern(); let edge = Edge::NodeEdge { - output_node: output_node_id, - input_node: input_node_id, + output_node, + input_node, }; self.validate_edge(&edge, EdgeExistence::DoesNotExist)?; { - let output_node = self.get_node_state_mut(output_node_id)?; + let output_node = self.get_node_state_mut(output_node)?; output_node.edges.add_output_edge(edge.clone())?; } - let input_node = self.get_node_state_mut(input_node_id)?; + let input_node = self.get_node_state_mut(input_node)?; input_node.edges.add_input_edge(edge)?; Ok(()) @@ -398,16 +391,12 @@ impl RenderGraph { /// /// # Panics /// - /// Panics if any invalid [`NodeLabel`] is given. + /// Panics if any invalid [`RenderLabel`] is given. /// /// # See also /// /// - [`try_add_node_edge`](Self::try_add_node_edge) for a fallible version. - pub fn add_node_edge( - &mut self, - output_node: impl Into, - input_node: impl Into, - ) { + pub fn add_node_edge(&mut self, output_node: impl RenderLabel, input_node: impl RenderLabel) { self.try_add_node_edge(output_node, input_node).unwrap(); } @@ -415,24 +404,24 @@ impl RenderGraph { /// happens. pub fn remove_node_edge( &mut self, - output_node: impl Into, - input_node: impl Into, + output_node: impl RenderLabel, + input_node: impl RenderLabel, ) -> Result<(), RenderGraphError> { - let output_node_id = self.get_node_id(output_node)?; - let input_node_id = self.get_node_id(input_node)?; + let output_node = output_node.intern(); + let input_node = input_node.intern(); let edge = Edge::NodeEdge { - output_node: output_node_id, - input_node: input_node_id, + output_node, + input_node, }; self.validate_edge(&edge, EdgeExistence::Exists)?; { - let output_node = self.get_node_state_mut(output_node_id)?; + let output_node = self.get_node_state_mut(output_node)?; output_node.edges.remove_output_edge(edge.clone())?; } - let input_node = self.get_node_state_mut(input_node_id)?; + let input_node = self.get_node_state_mut(input_node)?; input_node.edges.remove_input_edge(edge)?; Ok(()) @@ -537,24 +526,24 @@ impl RenderGraph { } /// Returns an iterator over the sub graphs. - pub fn iter_sub_graphs(&self) -> impl Iterator { - self.sub_graphs - .iter() - .map(|(name, graph)| (name.as_ref(), graph)) + pub fn iter_sub_graphs(&self) -> impl Iterator { + self.sub_graphs.iter().map(|(name, graph)| (*name, graph)) } /// Returns an iterator over the sub graphs, that allows modifying each value. - pub fn iter_sub_graphs_mut(&mut self) -> impl Iterator { + pub fn iter_sub_graphs_mut( + &mut self, + ) -> impl Iterator { self.sub_graphs .iter_mut() - .map(|(name, graph)| (name.as_ref(), graph)) + .map(|(name, graph)| (*name, graph)) } /// Returns an iterator over a tuple of the input edges and the corresponding output nodes /// for the node referenced by the label. pub fn iter_node_inputs( &self, - label: impl Into, + label: impl RenderLabel, ) -> Result, RenderGraphError> { let node = self.get_node_state(label)?; Ok(node @@ -562,16 +551,14 @@ impl RenderGraph { .input_edges() .iter() .map(|edge| (edge, edge.get_output_node())) - .map(move |(edge, output_node_id)| { - (edge, self.get_node_state(output_node_id).unwrap()) - })) + .map(move |(edge, output_node)| (edge, self.get_node_state(output_node).unwrap()))) } /// Returns an iterator over a tuple of the output edges and the corresponding input nodes /// for the node referenced by the label. pub fn iter_node_outputs( &self, - label: impl Into, + label: impl RenderLabel, ) -> Result, RenderGraphError> { let node = self.get_node_state(label)?; Ok(node @@ -579,66 +566,68 @@ impl RenderGraph { .output_edges() .iter() .map(|edge| (edge, edge.get_input_node())) - .map(move |(edge, input_node_id)| (edge, self.get_node_state(input_node_id).unwrap()))) + .map(move |(edge, input_node)| (edge, self.get_node_state(input_node).unwrap()))) } - /// Adds the `sub_graph` with the `name` to the graph. - /// If the name is already present replaces it instead. - pub fn add_sub_graph(&mut self, name: impl Into>, sub_graph: RenderGraph) { - self.sub_graphs.insert(name.into(), sub_graph); + /// Adds the `sub_graph` with the `label` to the graph. + /// If the label is already present replaces it instead. + pub fn add_sub_graph(&mut self, label: impl RenderSubGraph, sub_graph: RenderGraph) { + self.sub_graphs.insert(label.intern(), sub_graph); } - /// Removes the `sub_graph` with the `name` from the graph. - /// If the name does not exist then nothing happens. - pub fn remove_sub_graph(&mut self, name: impl Into>) { - self.sub_graphs.remove(&name.into()); + /// Removes the `sub_graph` with the `label` from the graph. + /// If the label does not exist then nothing happens. + pub fn remove_sub_graph(&mut self, label: impl RenderSubGraph) { + self.sub_graphs.remove(&label.intern()); } - /// Retrieves the sub graph corresponding to the `name`. - pub fn get_sub_graph(&self, name: impl AsRef) -> Option<&RenderGraph> { - self.sub_graphs.get(name.as_ref()) + /// Retrieves the sub graph corresponding to the `label`. + pub fn get_sub_graph(&self, label: impl RenderSubGraph) -> Option<&RenderGraph> { + self.sub_graphs.get(&label.intern()) } - /// Retrieves the sub graph corresponding to the `name` mutably. - pub fn get_sub_graph_mut(&mut self, name: impl AsRef) -> Option<&mut RenderGraph> { - self.sub_graphs.get_mut(name.as_ref()) + /// Retrieves the sub graph corresponding to the `label` mutably. + pub fn get_sub_graph_mut(&mut self, label: impl RenderSubGraph) -> Option<&mut RenderGraph> { + self.sub_graphs.get_mut(&label.intern()) } - /// Retrieves the sub graph corresponding to the `name`. + /// Retrieves the sub graph corresponding to the `label`. /// /// # Panics /// - /// Panics if any invalid node name is given. + /// Panics if any invalid subgraph label is given. /// /// # See also /// /// - [`get_sub_graph`](Self::get_sub_graph) for a fallible version. - pub fn sub_graph(&self, name: impl AsRef) -> &RenderGraph { + pub fn sub_graph(&self, label: impl RenderSubGraph) -> &RenderGraph { + let label = label.intern(); self.sub_graphs - .get(name.as_ref()) - .unwrap_or_else(|| panic!("Node {} not found in sub_graph", name.as_ref())) + .get(&label) + .unwrap_or_else(|| panic!("Subgraph {label:?} not found")) } - /// Retrieves the sub graph corresponding to the `name` mutably. + /// Retrieves the sub graph corresponding to the `label` mutably. /// /// # Panics /// - /// Panics if any invalid node name is given. + /// Panics if any invalid subgraph label is given. /// /// # See also /// /// - [`get_sub_graph_mut`](Self::get_sub_graph_mut) for a fallible version. - pub fn sub_graph_mut(&mut self, name: impl AsRef) -> &mut RenderGraph { + pub fn sub_graph_mut(&mut self, label: impl RenderSubGraph) -> &mut RenderGraph { + let label = label.intern(); self.sub_graphs - .get_mut(name.as_ref()) - .unwrap_or_else(|| panic!("Node {} not found in sub_graph", name.as_ref())) + .get_mut(&label) + .unwrap_or_else(|| panic!("Subgraph {label:?} not found")) } } impl Debug for RenderGraph { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for node in self.iter_nodes() { - writeln!(f, "{:?}", node.id)?; + writeln!(f, "{:?}", node.label)?; writeln!(f, " in: {:?}", node.input_slots)?; writeln!(f, " out: {:?}", node.output_slots)?; } @@ -680,14 +669,22 @@ impl Node for GraphInputNode { mod tests { use crate::{ render_graph::{ - Edge, Node, NodeId, NodeRunError, RenderGraph, RenderGraphContext, RenderGraphError, - SlotInfo, SlotType, + node::IntoRenderNodeArray, Edge, InternedRenderLabel, Node, NodeRunError, RenderGraph, + RenderGraphContext, RenderGraphError, RenderLabel, SlotInfo, SlotType, }, renderer::RenderContext, }; use bevy_ecs::world::{FromWorld, World}; use bevy_utils::HashSet; + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + enum TestLabels { + A, + B, + C, + D, + } + #[derive(Debug)] struct TestNode { inputs: Vec, @@ -726,65 +723,74 @@ mod tests { } } - fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet { + fn input_nodes(label: impl RenderLabel, graph: &RenderGraph) -> HashSet { graph - .iter_node_inputs(name) + .iter_node_inputs(label) .unwrap() - .map(|(_edge, node)| node.id) - .collect::>() + .map(|(_edge, node)| node.label) + .collect::>() } - fn output_nodes(name: &'static str, graph: &RenderGraph) -> HashSet { + fn output_nodes(label: impl RenderLabel, graph: &RenderGraph) -> HashSet { graph - .iter_node_outputs(name) + .iter_node_outputs(label) .unwrap() - .map(|(_edge, node)| node.id) - .collect::>() + .map(|(_edge, node)| node.label) + .collect::>() } #[test] fn test_graph_edges() { let mut graph = RenderGraph::default(); - let a_id = graph.add_node("A", TestNode::new(0, 1)); - let b_id = graph.add_node("B", TestNode::new(0, 1)); - let c_id = graph.add_node("C", TestNode::new(1, 1)); - let d_id = graph.add_node("D", TestNode::new(1, 0)); - - graph.add_slot_edge("A", "out_0", "C", "in_0"); - graph.add_node_edge("B", "C"); - graph.add_slot_edge("C", 0, "D", 0); - - assert!(input_nodes("A", &graph).is_empty(), "A has no inputs"); + graph.add_node(TestLabels::A, TestNode::new(0, 1)); + graph.add_node(TestLabels::B, TestNode::new(0, 1)); + graph.add_node(TestLabels::C, TestNode::new(1, 1)); + graph.add_node(TestLabels::D, TestNode::new(1, 0)); + + graph.add_slot_edge(TestLabels::A, "out_0", TestLabels::C, "in_0"); + graph.add_node_edge(TestLabels::B, TestLabels::C); + graph.add_slot_edge(TestLabels::C, 0, TestLabels::D, 0); + + assert!( + input_nodes(TestLabels::A, &graph).is_empty(), + "A has no inputs" + ); assert_eq!( - output_nodes("A", &graph), - HashSet::from_iter(vec![c_id]), + output_nodes(TestLabels::A, &graph), + HashSet::from_iter((TestLabels::C,).into_array()), "A outputs to C" ); - assert!(input_nodes("B", &graph).is_empty(), "B has no inputs"); + assert!( + input_nodes(TestLabels::B, &graph).is_empty(), + "B has no inputs" + ); assert_eq!( - output_nodes("B", &graph), - HashSet::from_iter(vec![c_id]), + output_nodes(TestLabels::B, &graph), + HashSet::from_iter((TestLabels::C,).into_array()), "B outputs to C" ); assert_eq!( - input_nodes("C", &graph), - HashSet::from_iter(vec![a_id, b_id]), + input_nodes(TestLabels::C, &graph), + HashSet::from_iter((TestLabels::A, TestLabels::B).into_array()), "A and B input to C" ); assert_eq!( - output_nodes("C", &graph), - HashSet::from_iter(vec![d_id]), + output_nodes(TestLabels::C, &graph), + HashSet::from_iter((TestLabels::D,).into_array()), "C outputs to D" ); assert_eq!( - input_nodes("D", &graph), - HashSet::from_iter(vec![c_id]), + input_nodes(TestLabels::D, &graph), + HashSet::from_iter((TestLabels::C,).into_array()), "C inputs to D" ); - assert!(output_nodes("D", &graph).is_empty(), "D has no outputs"); + assert!( + output_nodes(TestLabels::D, &graph).is_empty(), + "D has no outputs" + ); } #[test] @@ -806,12 +812,12 @@ mod tests { let mut graph = RenderGraph::default(); - graph.add_node("A", MyNode { value: 42 }); + graph.add_node(TestLabels::A, MyNode { value: 42 }); - let node: &MyNode = graph.get_node("A").unwrap(); + let node: &MyNode = graph.get_node(TestLabels::A).unwrap(); assert_eq!(node.value, 42, "node value matches"); - let result: Result<&TestNode, RenderGraphError> = graph.get_node("A"); + let result: Result<&TestNode, RenderGraphError> = graph.get_node(TestLabels::A); assert_eq!( result.unwrap_err(), RenderGraphError::WrongNodeType, @@ -823,17 +829,17 @@ mod tests { fn test_slot_already_occupied() { let mut graph = RenderGraph::default(); - graph.add_node("A", TestNode::new(0, 1)); - graph.add_node("B", TestNode::new(0, 1)); - graph.add_node("C", TestNode::new(1, 1)); + graph.add_node(TestLabels::A, TestNode::new(0, 1)); + graph.add_node(TestLabels::B, TestNode::new(0, 1)); + graph.add_node(TestLabels::C, TestNode::new(1, 1)); - graph.add_slot_edge("A", 0, "C", 0); + graph.add_slot_edge(TestLabels::A, 0, TestLabels::C, 0); assert_eq!( - graph.try_add_slot_edge("B", 0, "C", 0), + graph.try_add_slot_edge(TestLabels::B, 0, TestLabels::C, 0), Err(RenderGraphError::NodeInputSlotAlreadyOccupied { - node: graph.get_node_id("C").unwrap(), + node: TestLabels::C.intern(), input_slot: 0, - occupied_by_node: graph.get_node_id("A").unwrap(), + occupied_by_node: TestLabels::A.intern(), }), "Adding to a slot that is already occupied should return an error" ); @@ -843,16 +849,16 @@ mod tests { fn test_edge_already_exists() { let mut graph = RenderGraph::default(); - graph.add_node("A", TestNode::new(0, 1)); - graph.add_node("B", TestNode::new(1, 0)); + graph.add_node(TestLabels::A, TestNode::new(0, 1)); + graph.add_node(TestLabels::B, TestNode::new(1, 0)); - graph.add_slot_edge("A", 0, "B", 0); + graph.add_slot_edge(TestLabels::A, 0, TestLabels::B, 0); assert_eq!( - graph.try_add_slot_edge("A", 0, "B", 0), + graph.try_add_slot_edge(TestLabels::A, 0, TestLabels::B, 0), Err(RenderGraphError::EdgeAlreadyExists(Edge::SlotEdge { - output_node: graph.get_node_id("A").unwrap(), + output_node: TestLabels::A.intern(), output_index: 0, - input_node: graph.get_node_id("B").unwrap(), + input_node: TestLabels::B.intern(), input_index: 0, })), "Adding to a duplicate edge should return an error" @@ -879,30 +885,30 @@ mod tests { } let mut graph = RenderGraph::default(); - let a_id = graph.add_node("A", SimpleNode); - let b_id = graph.add_node("B", SimpleNode); - let c_id = graph.add_node("C", SimpleNode); + graph.add_node(TestLabels::A, SimpleNode); + graph.add_node(TestLabels::B, SimpleNode); + graph.add_node(TestLabels::C, SimpleNode); - graph.add_node_edges(&["A", "B", "C"]); + graph.add_node_edges((TestLabels::A, TestLabels::B, TestLabels::C)); assert_eq!( - output_nodes("A", &graph), - HashSet::from_iter(vec![b_id]), + output_nodes(TestLabels::A, &graph), + HashSet::from_iter((TestLabels::B,).into_array()), "A -> B" ); assert_eq!( - input_nodes("B", &graph), - HashSet::from_iter(vec![a_id]), + input_nodes(TestLabels::B, &graph), + HashSet::from_iter((TestLabels::A,).into_array()), "A -> B" ); assert_eq!( - output_nodes("B", &graph), - HashSet::from_iter(vec![c_id]), + output_nodes(TestLabels::B, &graph), + HashSet::from_iter((TestLabels::C,).into_array()), "B -> C" ); assert_eq!( - input_nodes("C", &graph), - HashSet::from_iter(vec![b_id]), + input_nodes(TestLabels::C, &graph), + HashSet::from_iter((TestLabels::B,).into_array()), "B -> C" ); } diff --git a/crates/bevy_render/src/render_graph/mod.rs b/crates/bevy_render/src/render_graph/mod.rs index 8ba77ec24528c..fbed33a23c6f4 100644 --- a/crates/bevy_render/src/render_graph/mod.rs +++ b/crates/bevy_render/src/render_graph/mod.rs @@ -16,33 +16,39 @@ use thiserror::Error; #[derive(Error, Debug, Eq, PartialEq)] pub enum RenderGraphError { - #[error("node does not exist")] - InvalidNode(NodeLabel), + #[error("node {0:?} does not exist")] + InvalidNode(InternedRenderLabel), #[error("output node slot does not exist")] InvalidOutputNodeSlot(SlotLabel), #[error("input node slot does not exist")] InvalidInputNodeSlot(SlotLabel), #[error("node does not match the given type")] WrongNodeType, - #[error("attempted to connect a node output slot to an incompatible input node slot")] + #[error("attempted to connect output slot {output_slot} from node {output_node:?} to incompatible input slot {input_slot} from node {input_node:?}")] MismatchedNodeSlots { - output_node: NodeId, + output_node: InternedRenderLabel, output_slot: usize, - input_node: NodeId, + input_node: InternedRenderLabel, input_slot: usize, }, #[error("attempted to add an edge that already exists")] EdgeAlreadyExists(Edge), #[error("attempted to remove an edge that does not exist")] EdgeDoesNotExist(Edge), - #[error("node has an unconnected input slot")] - UnconnectedNodeInputSlot { node: NodeId, input_slot: usize }, - #[error("node has an unconnected output slot")] - UnconnectedNodeOutputSlot { node: NodeId, output_slot: usize }, - #[error("node input slot already occupied")] + #[error("node {node:?} has an unconnected input slot {input_slot}")] + UnconnectedNodeInputSlot { + node: InternedRenderLabel, + input_slot: usize, + }, + #[error("node {node:?} has an unconnected output slot {output_slot}")] + UnconnectedNodeOutputSlot { + node: InternedRenderLabel, + output_slot: usize, + }, + #[error("node {node:?} input slot {input_slot} already occupied by {occupied_by_node:?}")] NodeInputSlotAlreadyOccupied { - node: NodeId, + node: InternedRenderLabel, input_slot: usize, - occupied_by_node: NodeId, + occupied_by_node: InternedRenderLabel, }, } diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index ef28794f9f49f..7dc07fcd24ced 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -1,5 +1,4 @@ use crate::{ - define_atomic_id, render_graph::{ Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, RunSubGraphError, SlotInfo, SlotInfos, @@ -10,11 +9,42 @@ use bevy_ecs::{ query::{QueryItem, QueryState, ReadOnlyQueryData}, world::{FromWorld, World}, }; +pub use bevy_utils::label::DynEq; +use bevy_utils::{all_tuples_with_size, define_label, intern::Interned}; use downcast_rs::{impl_downcast, Downcast}; -use std::{borrow::Cow, fmt::Debug}; +use std::fmt::Debug; use thiserror::Error; -define_atomic_id!(NodeId); +pub use bevy_render_macros::RenderLabel; + +use super::{InternedRenderSubGraph, RenderSubGraph}; + +define_label!( + /// A strongly-typed class of labels used to identify a [`Node`] in a render graph. + RenderLabel, + RENDER_LABEL_INTERNER +); + +/// A shorthand for `Interned`. +pub type InternedRenderLabel = Interned; + +pub trait IntoRenderNodeArray { + fn into_array(self) -> [InternedRenderLabel; N]; +} + +macro_rules! impl_render_label_tuples { + ($N: expr, $(($T: ident, $I: ident)),*) => { + impl<$($T: RenderLabel),*> IntoRenderNodeArray<$N> for ($($T,)*) { + #[inline] + fn into_array(self) -> [InternedRenderLabel; $N] { + let ($($I,)*) = self; + [$($I.intern(), )*] + } + } + } +} + +all_tuples_with_size!(impl_render_label_tuples, 1, 32, T, l); /// A render node that can be added to a [`RenderGraph`](super::RenderGraph). /// @@ -70,7 +100,7 @@ pub enum NodeRunError { /// A collection of input and output [`Edges`](Edge) for a [`Node`]. #[derive(Debug)] pub struct Edges { - id: NodeId, + label: InternedRenderLabel, input_edges: Vec, output_edges: Vec, } @@ -88,10 +118,10 @@ impl Edges { &self.output_edges } - /// Returns this node's id. + /// Returns this node's label. #[inline] - pub fn id(&self) -> NodeId { - self.id + pub fn label(&self) -> InternedRenderLabel { + self.label } /// Adds an edge to the `input_edges` if it does not already exist. @@ -156,7 +186,7 @@ impl Edges { }) .ok_or(RenderGraphError::UnconnectedNodeInputSlot { input_slot: index, - node: self.id, + node: self.label, }) } @@ -174,7 +204,7 @@ impl Edges { }) .ok_or(RenderGraphError::UnconnectedNodeOutputSlot { output_slot: index, - node: self.id, + node: self.label, }) } } @@ -184,8 +214,7 @@ impl Edges { /// /// The `input_slots` and `output_slots` are provided by the `node`. pub struct NodeState { - pub id: NodeId, - pub name: Option>, + pub label: InternedRenderLabel, /// The name of the type that implements [`Node`]. pub type_name: &'static str, pub node: Box, @@ -196,26 +225,25 @@ pub struct NodeState { impl Debug for NodeState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{:?} ({:?})", self.id, self.name) + writeln!(f, "{:?} ({:?})", self.label, self.type_name) } } impl NodeState { /// Creates an [`NodeState`] without edges, but the `input_slots` and `output_slots` /// are provided by the `node`. - pub fn new(id: NodeId, node: T) -> Self + pub fn new(label: InternedRenderLabel, node: T) -> Self where T: Node, { NodeState { - id, - name: None, + label, input_slots: node.input().into(), output_slots: node.output().into(), node: Box::new(node), type_name: std::any::type_name::(), edges: Edges { - id, + label, input_edges: Vec::new(), output_edges: Vec::new(), }, @@ -261,38 +289,6 @@ impl NodeState { } } -/// A [`NodeLabel`] is used to reference a [`NodeState`] by either its name or [`NodeId`] -/// inside the [`RenderGraph`](super::RenderGraph). -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum NodeLabel { - Id(NodeId), - Name(Cow<'static, str>), -} - -impl From<&NodeLabel> for NodeLabel { - fn from(value: &NodeLabel) -> Self { - value.clone() - } -} - -impl From for NodeLabel { - fn from(value: String) -> Self { - NodeLabel::Name(value.into()) - } -} - -impl From<&'static str> for NodeLabel { - fn from(value: &'static str) -> Self { - NodeLabel::Name(value.into()) - } -} - -impl From for NodeLabel { - fn from(value: NodeId) -> Self { - NodeLabel::Id(value) - } -} - /// A [`Node`] without any inputs, outputs and subgraphs, which does nothing when run. /// Used (as a label) to bundle multiple dependencies into one inside /// the [`RenderGraph`](super::RenderGraph). @@ -310,16 +306,16 @@ impl Node for EmptyNode { } } -/// A [`RenderGraph`](super::RenderGraph) [`Node`] that runs the configured graph name once. +/// A [`RenderGraph`](super::RenderGraph) [`Node`] that runs the configured subgraph once. /// This makes it easier to insert sub-graph runs into a graph. pub struct RunGraphOnViewNode { - graph_name: Cow<'static, str>, + sub_graph: InternedRenderSubGraph, } impl RunGraphOnViewNode { - pub fn new>>(graph_name: T) -> Self { + pub fn new(sub_graph: T) -> Self { Self { - graph_name: graph_name.into(), + sub_graph: sub_graph.intern(), } } } @@ -331,7 +327,7 @@ impl Node for RunGraphOnViewNode { _render_context: &mut RenderContext, _world: &World, ) -> Result<(), NodeRunError> { - graph.run_sub_graph(self.graph_name.clone(), vec![], Some(graph.view_entity()))?; + graph.run_sub_graph(self.sub_graph, vec![], Some(graph.view_entity()))?; Ok(()) } } diff --git a/crates/bevy_render/src/renderer/graph_runner.rs b/crates/bevy_render/src/renderer/graph_runner.rs index 077d0df8e938b..49319127bd96b 100644 --- a/crates/bevy_render/src/renderer/graph_runner.rs +++ b/crates/bevy_render/src/renderer/graph_runner.rs @@ -6,15 +6,13 @@ use bevy_utils::{ HashMap, }; -#[cfg(feature = "trace")] -use std::ops::Deref; use std::{borrow::Cow, collections::VecDeque}; use thiserror::Error; use crate::{ render_graph::{ - Edge, NodeId, NodeRunError, NodeState, RenderGraph, RenderGraphContext, SlotLabel, - SlotType, SlotValue, + Edge, InternedRenderLabel, InternedRenderSubGraph, NodeRunError, NodeState, RenderGraph, + RenderGraphContext, SlotLabel, SlotType, SlotValue, }, renderer::{RenderContext, RenderDevice}, }; @@ -31,11 +29,11 @@ pub enum RenderGraphRunnerError { slot_index: usize, slot_name: Cow<'static, str>, }, - #[error("graph (name: '{graph_name:?}') could not be run because slot '{slot_name}' at index {slot_index} has no value")] + #[error("graph '{sub_graph:?}' could not be run because slot '{slot_name}' at index {slot_index} has no value")] MissingInput { slot_index: usize, slot_name: Cow<'static, str>, - graph_name: Option>, + sub_graph: Option, }, #[error("attempted to use the wrong type for input slot")] MismatchedInputSlotType { @@ -48,7 +46,7 @@ pub enum RenderGraphRunnerError { "node (name: '{node_name:?}') has {slot_count} input slots, but was provided {value_count} values" )] MismatchedInputCount { - node_name: Option>, + node_name: InternedRenderLabel, slot_count: usize, value_count: usize, }, @@ -76,16 +74,17 @@ impl RenderGraphRunner { fn run_graph( graph: &RenderGraph, - graph_name: Option>, + sub_graph: Option, render_context: &mut RenderContext, world: &World, inputs: &[SlotValue], view_entity: Option, ) -> Result<(), RenderGraphRunnerError> { - let mut node_outputs: HashMap> = HashMap::default(); + let mut node_outputs: HashMap> = + HashMap::default(); #[cfg(feature = "trace")] - let span = if let Some(name) = &graph_name { - info_span!("run_graph", name = name.deref()) + let span = if let Some(label) = &sub_graph { + info_span!("run_graph", name = format!("{label:?}")) } else { info_span!("run_graph", name = "main_graph") }; @@ -116,28 +115,31 @@ impl RenderGraphRunner { return Err(RenderGraphRunnerError::MissingInput { slot_index: i, slot_name: input_slot.name.clone(), - graph_name, + sub_graph, }); } } - node_outputs.insert(input_node.id, input_values); + node_outputs.insert(input_node.label, input_values); - for (_, node_state) in graph.iter_node_outputs(input_node.id).expect("node exists") { + for (_, node_state) in graph + .iter_node_outputs(input_node.label) + .expect("node exists") + { node_queue.push_front(node_state); } } 'handle_node: while let Some(node_state) = node_queue.pop_back() { // skip nodes that are already processed - if node_outputs.contains_key(&node_state.id) { + if node_outputs.contains_key(&node_state.label) { continue; } let mut slot_indices_and_inputs: SmallVec<[(usize, SlotValue); 4]> = SmallVec::new(); // check if all dependencies have finished running for (edge, input_node) in graph - .iter_node_inputs(node_state.id) + .iter_node_inputs(node_state.label) .expect("node is in graph") { match edge { @@ -146,7 +148,7 @@ impl RenderGraphRunner { input_index, .. } => { - if let Some(outputs) = node_outputs.get(&input_node.id) { + if let Some(outputs) = node_outputs.get(&input_node.label) { slot_indices_and_inputs .push((*input_index, outputs[*output_index].clone())); } else { @@ -155,7 +157,7 @@ impl RenderGraphRunner { } } Edge::NodeEdge { .. } => { - if !node_outputs.contains_key(&input_node.id) { + if !node_outputs.contains_key(&input_node.label) { node_queue.push_front(node_state); continue 'handle_node; } @@ -172,7 +174,7 @@ impl RenderGraphRunner { if inputs.len() != node_state.input_slots.len() { return Err(RenderGraphRunnerError::MismatchedInputCount { - node_name: node_state.name.clone(), + node_name: node_state.label, slot_count: node_state.input_slots.len(), value_count: inputs.len(), }); @@ -195,11 +197,11 @@ impl RenderGraphRunner { for run_sub_graph in context.finish() { let sub_graph = graph - .get_sub_graph(&run_sub_graph.name) + .get_sub_graph(run_sub_graph.sub_graph) .expect("sub graph exists because it was validated when queued."); Self::run_graph( sub_graph, - Some(run_sub_graph.name), + Some(run_sub_graph.sub_graph), render_context, world, &run_sub_graph.inputs, @@ -221,9 +223,12 @@ impl RenderGraphRunner { }); } } - node_outputs.insert(node_state.id, values); + node_outputs.insert(node_state.label, values); - for (_, node_state) in graph.iter_node_outputs(node_state.id).expect("node exists") { + for (_, node_state) in graph + .iter_node_outputs(node_state.label) + .expect("node exists") + { node_queue.push_front(node_state); } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index b4d333735b6ef..e495ac53bb6b3 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,6 +2,8 @@ mod pipeline; mod render_pass; mod ui_material_pipeline; +use bevy_core_pipeline::core_2d::graph::{Labels2d, SubGraph2d}; +use bevy_core_pipeline::core_3d::graph::{Labels3d, SubGraph3d}; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; use bevy_hierarchy::Parent; use bevy_render::{ @@ -13,6 +15,7 @@ pub use pipeline::*; pub use render_pass::*; pub use ui_material_pipeline::*; +use crate::graph::{LabelsUi, SubGraphUi}; use crate::{ BackgroundColor, BorderColor, CalculatedClip, ContentSize, Node, Style, UiImage, UiScale, Val, }; @@ -43,14 +46,15 @@ use bevy_utils::{EntityHashMap, FloatOrd, HashMap}; use bytemuck::{Pod, Zeroable}; use std::ops::Range; -pub mod node { - pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; -} +pub mod graph { + use bevy_render::render_graph::{RenderLabel, RenderSubGraph}; + + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderSubGraph)] + pub struct SubGraphUi; -pub mod draw_ui_graph { - pub const NAME: &str = "draw_ui"; - pub mod node { - pub const UI_PASS: &str = "ui_pass"; + #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] + pub enum LabelsUi { + UiPass, } } @@ -103,51 +107,27 @@ pub fn build_ui_render(app: &mut App) { let ui_graph_3d = get_ui_graph(render_app); let mut graph = render_app.world.resource_mut::(); - if let Some(graph_2d) = graph.get_sub_graph_mut(bevy_core_pipeline::core_2d::graph::NAME) { - graph_2d.add_sub_graph(draw_ui_graph::NAME, ui_graph_2d); - graph_2d.add_node( - draw_ui_graph::node::UI_PASS, - RunGraphOnViewNode::new(draw_ui_graph::NAME), - ); - graph_2d.add_node_edge( - bevy_core_pipeline::core_2d::graph::node::MAIN_PASS, - draw_ui_graph::node::UI_PASS, - ); - graph_2d.add_node_edge( - bevy_core_pipeline::core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING, - draw_ui_graph::node::UI_PASS, - ); - graph_2d.add_node_edge( - draw_ui_graph::node::UI_PASS, - bevy_core_pipeline::core_2d::graph::node::UPSCALING, - ); + if let Some(graph_2d) = graph.get_sub_graph_mut(SubGraph2d) { + graph_2d.add_sub_graph(SubGraphUi, ui_graph_2d); + graph_2d.add_node(LabelsUi::UiPass, RunGraphOnViewNode::new(SubGraphUi)); + graph_2d.add_node_edge(Labels2d::MainPass, LabelsUi::UiPass); + graph_2d.add_node_edge(Labels2d::EndMainPassPostProcessing, LabelsUi::UiPass); + graph_2d.add_node_edge(LabelsUi::UiPass, Labels2d::Upscaling); } - if let Some(graph_3d) = graph.get_sub_graph_mut(bevy_core_pipeline::core_3d::graph::NAME) { - graph_3d.add_sub_graph(draw_ui_graph::NAME, ui_graph_3d); - graph_3d.add_node( - draw_ui_graph::node::UI_PASS, - RunGraphOnViewNode::new(draw_ui_graph::NAME), - ); - graph_3d.add_node_edge( - bevy_core_pipeline::core_3d::graph::node::END_MAIN_PASS, - draw_ui_graph::node::UI_PASS, - ); - graph_3d.add_node_edge( - bevy_core_pipeline::core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, - draw_ui_graph::node::UI_PASS, - ); - graph_3d.add_node_edge( - draw_ui_graph::node::UI_PASS, - bevy_core_pipeline::core_3d::graph::node::UPSCALING, - ); + if let Some(graph_3d) = graph.get_sub_graph_mut(SubGraph3d) { + graph_3d.add_sub_graph(SubGraphUi, ui_graph_3d); + graph_3d.add_node(LabelsUi::UiPass, RunGraphOnViewNode::new(SubGraphUi)); + graph_3d.add_node_edge(Labels3d::EndMainPass, LabelsUi::UiPass); + graph_3d.add_node_edge(Labels3d::EndMainPassPostProcessing, LabelsUi::UiPass); + graph_3d.add_node_edge(LabelsUi::UiPass, Labels3d::Upscaling); } } fn get_ui_graph(render_app: &mut App) -> RenderGraph { let ui_pass_node = UiPassNode::new(&mut render_app.world); let mut ui_graph = RenderGraph::default(); - ui_graph.add_node(draw_ui_graph::node::UI_PASS, ui_pass_node); + ui_graph.add_node(LabelsUi::UiPass, ui_pass_node); ui_graph } diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index 8248e4e9b14f3..fd437172d2439 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -9,7 +9,7 @@ use bevy::{ extract_resource::{ExtractResource, ExtractResourcePlugin}, render_asset::RenderAssetPersistencePolicy, render_asset::RenderAssets, - render_graph::{self, RenderGraph}, + render_graph::{self, RenderGraph, RenderLabel}, render_resource::*, renderer::{RenderContext, RenderDevice}, Render, RenderApp, RenderSet, @@ -70,6 +70,9 @@ fn setup(mut commands: Commands, mut images: ResMut>) { pub struct GameOfLifeComputePlugin; +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +pub struct GameOfLifeLabel; + impl Plugin for GameOfLifeComputePlugin { fn build(&self, app: &mut App) { // Extract the game of life image resource from the main world into the render world @@ -82,11 +85,8 @@ impl Plugin for GameOfLifeComputePlugin { ); let mut render_graph = render_app.world.resource_mut::(); - render_graph.add_node("game_of_life", GameOfLifeNode::default()); - render_graph.add_node_edge( - "game_of_life", - bevy::render::main_graph::node::CAMERA_DRIVER, - ); + render_graph.add_node(GameOfLifeLabel, GameOfLifeNode::default()); + render_graph.add_node_edge(GameOfLifeLabel, bevy::render::graph::CameraDriverLabel); } fn finish(&self, app: &mut App) { diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index f2d15486b9e78..46c1f4811c700 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -6,7 +6,10 @@ //! This is a fairly low level example and assumes some familiarity with rendering concepts and wgpu. use bevy::{ - core_pipeline::{core_3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state}, + core_pipeline::{ + core_3d::graph::{Labels3d, SubGraph3d}, + fullscreen_vertex_shader::fullscreen_shader_vertex_state, + }, ecs::query::QueryItem, prelude::*, render::{ @@ -14,7 +17,7 @@ use bevy::{ ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, }, render_graph::{ - NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner, + NodeRunError, RenderGraphApp, RenderGraphContext, RenderLabel, ViewNode, ViewNodeRunner, }, render_resource::{ binding_types::{sampler, texture_2d, uniform_buffer}, @@ -74,20 +77,20 @@ impl Plugin for PostProcessPlugin { // The [`ViewNodeRunner`] is a special [`Node`] that will automatically run the node for each view // matching the [`ViewQuery`] .add_render_graph_node::>( - // Specify the name of the graph, in this case we want the graph for 3d - core_3d::graph::NAME, - // It also needs the name of the node - PostProcessNode::NAME, + // Specify the label of the graph, in this case we want the graph for 3d + SubGraph3d, + // It also needs the label of the node + PostProcessLabel, ) .add_render_graph_edges( - core_3d::graph::NAME, + SubGraph3d, // Specify the node ordering. // This will automatically create all required node edges to enforce the given ordering. - &[ - core_3d::graph::node::TONEMAPPING, - PostProcessNode::NAME, - core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, - ], + ( + Labels3d::Tonemapping, + PostProcessLabel, + Labels3d::EndMainPassPostProcessing, + ), ); } @@ -103,12 +106,12 @@ impl Plugin for PostProcessPlugin { } } +#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] +pub struct PostProcessLabel; + // The post process node used for the render graph #[derive(Default)] struct PostProcessNode; -impl PostProcessNode { - pub const NAME: &'static str = "post_process"; -} // The ViewNode trait is required by the ViewNodeRunner impl ViewNode for PostProcessNode {