From 41cee45eb5218351058c5a7faf7dada3554f38ef Mon Sep 17 00:00:00 2001 From: Lixou <82600264+DasLixou@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:51:19 +0100 Subject: [PATCH] RenderGraph Labelization (#10644) # Objective The whole `Cow<'static, str>` naming for nodes and subgraphs in `RenderGraph` is a mess. ## Solution Replaces hardcoded and potentially overlapping strings for nodes and subgraphs inside `RenderGraph` with bevy's labelsystem. --- ## Changelog * Two new labels: `RenderLabel` and `RenderSubGraph`. * Replaced all uses for hardcoded strings with those labels * Moved `Taa` label from its own mod to all the other `Labels3d` * `add_render_graph_edges` now needs a tuple of labels * Moved `ScreenSpaceAmbientOcclusion` label from its own mod with the `ShadowPass` label to `LabelsPbr` * Removed `NodeId` * Renamed `Edges.id()` to `Edges.label()` * Removed `NodeLabel` * Changed examples according to the new label system * Introduced new `RenderLabel`s: `Labels2d`, `Labels3d`, `LabelsPbr`, `LabelsUi` * Introduced new `RenderSubGraph`s: `SubGraph2d`, `SubGraph3d`, `SubGraphUi` * Removed `Reflect` and `Default` derive from `CameraRenderGraph` component struct * Improved some error messages ## Migration Guide For Nodes and SubGraphs, instead of using hardcoded strings, you now pass labels, which can be derived with structs and enums. ```rs // old #[derive(Default)] struct MyRenderNode; impl MyRenderNode { pub const NAME: &'static str = "my_render_node" } render_app .add_render_graph_node::>( core_3d::graph::NAME, MyRenderNode::NAME, ) .add_render_graph_edges( core_3d::graph::NAME, &[ core_3d::graph::node::TONEMAPPING, MyRenderNode::NAME, core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, ], ); // new use bevy::core_pipeline::core_3d::graph::{Labels3d, SubGraph3d}; #[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)] pub struct MyRenderLabel; #[derive(Default)] struct MyRenderNode; render_app .add_render_graph_node::>( SubGraph3d, MyRenderLabel, ) .add_render_graph_edges( SubGraph3d, ( Labels3d::Tonemapping, MyRenderLabel, Labels3d::EndMainPassPostProcessing, ), ); ``` ### SubGraphs #### in `bevy_core_pipeline::core_2d::graph` | old string-based path | new label | |-----------------------|-----------| | `NAME` | `SubGraph2d` | #### in `bevy_core_pipeline::core_3d::graph` | old string-based path | new label | |-----------------------|-----------| | `NAME` | `SubGraph3d` | #### in `bevy_ui::render` | old string-based path | new label | |-----------------------|-----------| | `draw_ui_graph::NAME` | `graph::SubGraphUi` | ### Nodes #### in `bevy_core_pipeline::core_2d::graph` | old string-based path | new label | |-----------------------|-----------| | `node::MSAA_WRITEBACK` | `Labels2d::MsaaWriteback` | | `node::MAIN_PASS` | `Labels2d::MainPass` | | `node::BLOOM` | `Labels2d::Bloom` | | `node::TONEMAPPING` | `Labels2d::Tonemapping` | | `node::FXAA` | `Labels2d::Fxaa` | | `node::UPSCALING` | `Labels2d::Upscaling` | | `node::CONTRAST_ADAPTIVE_SHARPENING` | `Labels2d::ConstrastAdaptiveSharpening` | | `node::END_MAIN_PASS_POST_PROCESSING` | `Labels2d::EndMainPassPostProcessing` | #### in `bevy_core_pipeline::core_3d::graph` | old string-based path | new label | |-----------------------|-----------| | `node::MSAA_WRITEBACK` | `Labels3d::MsaaWriteback` | | `node::PREPASS` | `Labels3d::Prepass` | | `node::DEFERRED_PREPASS` | `Labels3d::DeferredPrepass` | | `node::COPY_DEFERRED_LIGHTING_ID` | `Labels3d::CopyDeferredLightingId` | | `node::END_PREPASSES` | `Labels3d::EndPrepasses` | | `node::START_MAIN_PASS` | `Labels3d::StartMainPass` | | `node::MAIN_OPAQUE_PASS` | `Labels3d::MainOpaquePass` | | `node::MAIN_TRANSMISSIVE_PASS` | `Labels3d::MainTransmissivePass` | | `node::MAIN_TRANSPARENT_PASS` | `Labels3d::MainTransparentPass` | | `node::END_MAIN_PASS` | `Labels3d::EndMainPass` | | `node::BLOOM` | `Labels3d::Bloom` | | `node::TONEMAPPING` | `Labels3d::Tonemapping` | | `node::FXAA` | `Labels3d::Fxaa` | | `node::UPSCALING` | `Labels3d::Upscaling` | | `node::CONTRAST_ADAPTIVE_SHARPENING` | `Labels3d::ContrastAdaptiveSharpening` | | `node::END_MAIN_PASS_POST_PROCESSING` | `Labels3d::EndMainPassPostProcessing` | #### in `bevy_core_pipeline` | old string-based path | new label | |-----------------------|-----------| | `taa::draw_3d_graph::node::TAA` | `Labels3d::Taa` | #### in `bevy_pbr` | old string-based path | new label | |-----------------------|-----------| | `draw_3d_graph::node::SHADOW_PASS` | `LabelsPbr::ShadowPass` | | `ssao::draw_3d_graph::node::SCREEN_SPACE_AMBIENT_OCCLUSION` | `LabelsPbr::ScreenSpaceAmbientOcclusion` | | `deferred::DEFFERED_LIGHTING_PASS` | `LabelsPbr::DeferredLightingPass` | #### in `bevy_render` | old string-based path | new label | |-----------------------|-----------| | `main_graph::node::CAMERA_DRIVER` | `graph::CameraDriverLabel` | #### in `bevy_ui::render` | old string-based path | new label | |-----------------------|-----------| | `draw_ui_graph::node::UI_PASS` | `graph::LabelsUi::UiPass` | --- ## Future work * Make `NodeSlot`s also use types. Ideally, we have an enum with unit variants where every variant resembles one slot. Then to make sure you are using the right slot enum and make rust-analyzer play nicely with it, we should make an associated type in the `Node` trait. With today's system, we can introduce 3rd party slots to a node, and i wasnt sure if this was used, so I didn't do this in this PR. ## Unresolved Questions When looking at the `post_processing` example, we have a struct for the label and a struct for the node, this seems like boilerplate and on discord, @IceSentry (sowy for the ping) [asked](https://discord.com/channels/691052431525675048/743663924229963868/1175197016947699742) if a node could automatically introduce a label (or i completely misunderstood that). The problem with that is, that nodes like `EmptyNode` exist multiple times *inside the same* (sub)graph, so there we need extern labels to distinguish between those. Hopefully we can find a way to reduce boilerplate and still have everything unique. For EmptyNode, we could maybe make a macro which implements an "empty node" for a type, but for nodes which contain code and need to be present multiple times, this could get nasty... --- crates/bevy_core_pipeline/src/bloom/mod.rs | 34 +- .../src/contrast_adaptive_sharpening/mod.rs | 46 +- .../src/core_2d/camera_2d.rs | 11 +- crates/bevy_core_pipeline/src/core_2d/mod.rs | 57 +- .../src/core_3d/camera_3d.rs | 4 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 116 ++-- crates/bevy_core_pipeline/src/fxaa/mod.rs | 32 +- .../bevy_core_pipeline/src/msaa_writeback.rs | 18 +- crates/bevy_core_pipeline/src/taa/mod.rs | 27 +- crates/bevy_pbr/src/deferred/mod.rs | 21 +- crates/bevy_pbr/src/lib.rs | 26 +- crates/bevy_pbr/src/ssao/mod.rs | 25 +- crates/bevy_render/macros/src/lib.rs | 39 +- crates/bevy_render/src/camera/camera.rs | 20 +- .../src/camera/camera_driver_node.rs | 6 +- crates/bevy_render/src/camera/mod.rs | 3 +- crates/bevy_render/src/lib.rs | 9 +- crates/bevy_render/src/render_graph/app.rs | 66 ++- .../bevy_render/src/render_graph/context.rs | 28 +- crates/bevy_render/src/render_graph/edge.rs | 14 +- crates/bevy_render/src/render_graph/graph.rs | 498 +++++++++--------- crates/bevy_render/src/render_graph/mod.rs | 30 +- crates/bevy_render/src/render_graph/node.rs | 102 ++-- .../bevy_render/src/renderer/graph_runner.rs | 51 +- crates/bevy_ui/src/render/mod.rs | 68 +-- .../shader/compute_shader_game_of_life.rs | 12 +- examples/shader/post_processing.rs | 33 +- 27 files changed, 725 insertions(+), 671 deletions(-) diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index e0ddd2098b2c8a..ea2d1a8be6ef3c 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 6e512b873572a2..94a5ae08aa047a 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 d76c0c315c0b6f..be3c23b57dc18a 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 f9e9fb88dcb97f..bff795bcf06adf 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 0b8e21da0eaee0..f58f148c0592c7 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 5d8f067bf3c0e5..9e542d6c3240a5 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 c7777cd33ccf40..0f1a843a9edcac 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 06f294dae0e7ff..17af382e864fb4 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 b0eb4f3057e698..3cbfcc2d238ab2 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 11b75e5a556d68..c25a46eaad40ff 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 97fd0a0ecc5bdf..c1556c8d360d1b 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 d033092f06e660..daae9db3ec28c3 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 97126ba830bf46..c0d04e6c776a38 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 540f0fc3e30f5c..a2fff3edf0468b 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 9f24b72fde862a..5845f216bb1bc0 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 214ced79d25fcf..09961a28a7f6ef 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 5d31c855a0a653..656ba9e12aa6a4 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 3d301348a339e2..97ababa312b73f 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 da983fdc7fbae0..e62917465d6b53 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 4ba85fca578921..fa9943bb4dd02c 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 0263ede658368e..e62489ded6b810 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 8ba77ec24528c4..fbed33a23c6f4f 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 ef28794f9f49fe..7dc07fcd24ced0 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 077d0df8e938b4..49319127bd96b6 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 d8f26f95d117c8..613aa728ed9b96 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, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiImage, UiScale, Val, @@ -42,14 +45,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, } } @@ -102,51 +106,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 977215306b781e..8d7125505d9190 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::RenderAssetUsages, 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 f2d15486b9e78a..46c1f4811c700c 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 {