Add RenderGraphApp to simplify adding render nodes (#8007)
# Objective - Adding a node to the render_graph can be quite verbose and error prone because there's a lot of moving parts to it. ## Solution - Encapsulate this in a simple utility method - Mostly intended for optional nodes that have specific ordering - Requires that the `Node` impl `FromWorld`, but every internal node is built using a new function taking a `&mut World` so it was essentially already `FromWorld` - Use it for the bloom, fxaa and taa, nodes. - The main nodes don't use it because they rely more on the order of many nodes being added --- ## Changelog - Impl `FromWorld` for `BloomNode`, `FxaaNode` and `TaaNode` - Added `RenderGraph::add_node_edges()` - Added `RenderGraph::sub_graph()` - Added `RenderGraph::sub_graph_mut()` - Added `RenderGraphApp`, `RenderGraphApp::add_render_graph_node`, `RenderGraphApp::add_render_graph_edges`, `RenderGraphApp::add_render_graph_edge` ## Notes ~~This was taken out of https://github.com/bevyengine/bevy/pull/7995 because it works on it's own. Once the linked PR is done, the new `add_node()` will be simplified a bit since the input/output params won't be necessary.~~ This feature will be useful in most of the upcoming render nodes so it's impact will be more relevant at that point. Partially fixes #7985 ## Future work * Add a way to automatically label nodes or at least make it part of the trait. This would remove one more field from the functions added in this PR * Use it in the main pass 2d/3d --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
		
							parent
							
								
									5c7abb0579
								
							
						
					
					
						commit
						614de3019c
					
				@ -16,7 +16,7 @@ use bevy_render::{
 | 
			
		||||
        ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin,
 | 
			
		||||
    },
 | 
			
		||||
    prelude::Color,
 | 
			
		||||
    render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
 | 
			
		||||
    render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
 | 
			
		||||
    render_resource::*,
 | 
			
		||||
    renderer::{RenderContext, RenderDevice},
 | 
			
		||||
    texture::{CachedTexture, TextureCache},
 | 
			
		||||
@ -71,45 +71,27 @@ impl Plugin for BloomPlugin {
 | 
			
		||||
                    prepare_upsampling_pipeline.in_set(RenderSet::Prepare),
 | 
			
		||||
                    queue_bloom_bind_groups.in_set(RenderSet::Queue),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
            // Add bloom to the 3d render graph
 | 
			
		||||
            .add_render_graph_node::<BloomNode>(core_3d::graph::NAME, core_3d::graph::node::BLOOM)
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                &[
 | 
			
		||||
                    core_3d::graph::node::END_MAIN_PASS,
 | 
			
		||||
                    core_3d::graph::node::BLOOM,
 | 
			
		||||
                    core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
                ],
 | 
			
		||||
            )
 | 
			
		||||
            // Add bloom to the 2d render graph
 | 
			
		||||
            .add_render_graph_node::<BloomNode>(core_2d::graph::NAME, core_2d::graph::node::BLOOM)
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                &[
 | 
			
		||||
                    core_2d::graph::node::MAIN_PASS,
 | 
			
		||||
                    core_2d::graph::node::BLOOM,
 | 
			
		||||
                    core_2d::graph::node::TONEMAPPING,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        // Add bloom to the 3d render graph
 | 
			
		||||
        {
 | 
			
		||||
            let bloom_node = BloomNode::new(&mut render_app.world);
 | 
			
		||||
            let mut graph = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let draw_3d_graph = graph
 | 
			
		||||
                .get_sub_graph_mut(crate::core_3d::graph::NAME)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            draw_3d_graph.add_node(core_3d::graph::node::BLOOM, bloom_node);
 | 
			
		||||
            // MAIN_PASS -> BLOOM -> TONEMAPPING
 | 
			
		||||
            draw_3d_graph.add_node_edge(
 | 
			
		||||
                crate::core_3d::graph::node::END_MAIN_PASS,
 | 
			
		||||
                core_3d::graph::node::BLOOM,
 | 
			
		||||
            );
 | 
			
		||||
            draw_3d_graph.add_node_edge(
 | 
			
		||||
                core_3d::graph::node::BLOOM,
 | 
			
		||||
                crate::core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add bloom to the 2d render graph
 | 
			
		||||
        {
 | 
			
		||||
            let bloom_node = BloomNode::new(&mut render_app.world);
 | 
			
		||||
            let mut graph = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let draw_2d_graph = graph
 | 
			
		||||
                .get_sub_graph_mut(crate::core_2d::graph::NAME)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            draw_2d_graph.add_node(core_2d::graph::node::BLOOM, bloom_node);
 | 
			
		||||
            // MAIN_PASS -> BLOOM -> TONEMAPPING
 | 
			
		||||
            draw_2d_graph.add_node_edge(
 | 
			
		||||
                crate::core_2d::graph::node::MAIN_PASS,
 | 
			
		||||
                core_2d::graph::node::BLOOM,
 | 
			
		||||
            );
 | 
			
		||||
            draw_2d_graph.add_node_edge(
 | 
			
		||||
                core_2d::graph::node::BLOOM,
 | 
			
		||||
                crate::core_2d::graph::node::TONEMAPPING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -126,8 +108,8 @@ pub struct BloomNode {
 | 
			
		||||
    )>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BloomNode {
 | 
			
		||||
    pub fn new(world: &mut World) -> Self {
 | 
			
		||||
impl FromWorld for BloomNode {
 | 
			
		||||
    fn from_world(world: &mut World) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            view_query: QueryState::new(world),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ use bevy_reflect::{Reflect, TypeUuid};
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
 | 
			
		||||
    prelude::Camera,
 | 
			
		||||
    render_graph::RenderGraph,
 | 
			
		||||
    render_graph::RenderGraphApp,
 | 
			
		||||
    render_resource::*,
 | 
			
		||||
    renderer::RenderDevice,
 | 
			
		||||
    texture::BevyDefault,
 | 
			
		||||
@ -114,47 +114,47 @@ impl Plugin for CASPlugin {
 | 
			
		||||
        render_app
 | 
			
		||||
            .init_resource::<CASPipeline>()
 | 
			
		||||
            .init_resource::<SpecializedRenderPipelines<CASPipeline>>()
 | 
			
		||||
            .add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare));
 | 
			
		||||
        {
 | 
			
		||||
            let cas_node = CASNode::new(&mut render_app.world);
 | 
			
		||||
            let mut binding = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap();
 | 
			
		||||
 | 
			
		||||
            graph.add_node(core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING, cas_node);
 | 
			
		||||
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            .add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare))
 | 
			
		||||
            // 3d
 | 
			
		||||
            .add_render_graph_node::<CASNode>(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
                core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                core_3d::graph::node::FXAA,
 | 
			
		||||
                core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
                core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            let cas_node = CASNode::new(&mut render_app.world);
 | 
			
		||||
            let mut binding = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap();
 | 
			
		||||
 | 
			
		||||
            graph.add_node(core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING, cas_node);
 | 
			
		||||
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            )
 | 
			
		||||
            // 2d
 | 
			
		||||
            .add_render_graph_node::<CASNode>(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                core_2d::graph::node::TONEMAPPING,
 | 
			
		||||
                core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                core_2d::graph::node::FXAA,
 | 
			
		||||
                core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edge(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
 | 
			
		||||
                core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,8 +28,8 @@ pub struct CASNode {
 | 
			
		||||
    cached_bind_group: Mutex<Option<(BufferId, TextureViewId, BindGroup)>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CASNode {
 | 
			
		||||
    pub fn new(world: &mut World) -> Self {
 | 
			
		||||
impl FromWorld for CASNode {
 | 
			
		||||
    fn from_world(world: &mut World) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            query: QueryState::new(world),
 | 
			
		||||
            cached_bind_group: Mutex::new(None),
 | 
			
		||||
 | 
			
		||||
@ -74,15 +74,14 @@ impl Plugin for Core2dPlugin {
 | 
			
		||||
        draw_2d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
 | 
			
		||||
        draw_2d_graph.add_node(graph::node::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
 | 
			
		||||
        draw_2d_graph.add_node(graph::node::UPSCALING, upscaling);
 | 
			
		||||
        draw_2d_graph.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING);
 | 
			
		||||
        draw_2d_graph.add_node_edge(
 | 
			
		||||
 | 
			
		||||
        draw_2d_graph.add_node_edges(&[
 | 
			
		||||
            graph::node::MAIN_PASS,
 | 
			
		||||
            graph::node::TONEMAPPING,
 | 
			
		||||
            graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
        );
 | 
			
		||||
        draw_2d_graph.add_node_edge(
 | 
			
		||||
            graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            graph::node::UPSCALING,
 | 
			
		||||
        );
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        graph.add_sub_graph(graph::NAME, draw_2d_graph);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -106,25 +106,17 @@ impl Plugin for Core3dPlugin {
 | 
			
		||||
        draw_3d_graph.add_node(graph::node::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
 | 
			
		||||
        draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);
 | 
			
		||||
 | 
			
		||||
        draw_3d_graph.add_node_edge(graph::node::PREPASS, graph::node::START_MAIN_PASS);
 | 
			
		||||
        draw_3d_graph.add_node_edge(graph::node::START_MAIN_PASS, graph::node::MAIN_OPAQUE_PASS);
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
        draw_3d_graph.add_node_edges(&[
 | 
			
		||||
            graph::node::PREPASS,
 | 
			
		||||
            graph::node::START_MAIN_PASS,
 | 
			
		||||
            graph::node::MAIN_OPAQUE_PASS,
 | 
			
		||||
            graph::node::MAIN_TRANSPARENT_PASS,
 | 
			
		||||
        );
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
            graph::node::MAIN_TRANSPARENT_PASS,
 | 
			
		||||
            graph::node::END_MAIN_PASS,
 | 
			
		||||
        );
 | 
			
		||||
        draw_3d_graph.add_node_edge(graph::node::END_MAIN_PASS, graph::node::TONEMAPPING);
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
            graph::node::TONEMAPPING,
 | 
			
		||||
            graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
        );
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
            graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            graph::node::UPSCALING,
 | 
			
		||||
        );
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        graph.add_sub_graph(graph::NAME, draw_3d_graph);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ use bevy_reflect::{
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    extract_component::{ExtractComponent, ExtractComponentPlugin},
 | 
			
		||||
    prelude::Camera,
 | 
			
		||||
    render_graph::RenderGraph,
 | 
			
		||||
    render_graph::RenderGraphApp,
 | 
			
		||||
    render_resource::*,
 | 
			
		||||
    renderer::RenderDevice,
 | 
			
		||||
    texture::BevyDefault,
 | 
			
		||||
@ -90,40 +90,25 @@ impl Plugin for FxaaPlugin {
 | 
			
		||||
        render_app
 | 
			
		||||
            .init_resource::<FxaaPipeline>()
 | 
			
		||||
            .init_resource::<SpecializedRenderPipelines<FxaaPipeline>>()
 | 
			
		||||
            .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare));
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let fxaa_node = FxaaNode::new(&mut render_app.world);
 | 
			
		||||
            let mut binding = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap();
 | 
			
		||||
 | 
			
		||||
            graph.add_node(core_3d::graph::node::FXAA, fxaa_node);
 | 
			
		||||
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
                core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
                core_3d::graph::node::FXAA,
 | 
			
		||||
            .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare))
 | 
			
		||||
            .add_render_graph_node::<FxaaNode>(core_3d::graph::NAME, core_3d::graph::node::FXAA)
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                &[
 | 
			
		||||
                    core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
                    core_3d::graph::node::FXAA,
 | 
			
		||||
                    core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
                ],
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_node::<FxaaNode>(core_2d::graph::NAME, core_2d::graph::node::FXAA)
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_2d::graph::NAME,
 | 
			
		||||
                &[
 | 
			
		||||
                    core_2d::graph::node::TONEMAPPING,
 | 
			
		||||
                    core_2d::graph::node::FXAA,
 | 
			
		||||
                    core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
                core_3d::graph::node::FXAA,
 | 
			
		||||
                core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            let fxaa_node = FxaaNode::new(&mut render_app.world);
 | 
			
		||||
            let mut binding = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
            let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap();
 | 
			
		||||
 | 
			
		||||
            graph.add_node(core_2d::graph::node::FXAA, fxaa_node);
 | 
			
		||||
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
                core_2d::graph::node::TONEMAPPING,
 | 
			
		||||
                core_2d::graph::node::FXAA,
 | 
			
		||||
            );
 | 
			
		||||
            graph.add_node_edge(
 | 
			
		||||
                core_2d::graph::node::FXAA,
 | 
			
		||||
                core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,8 @@ pub struct FxaaNode {
 | 
			
		||||
    cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FxaaNode {
 | 
			
		||||
    pub fn new(world: &mut World) -> Self {
 | 
			
		||||
impl FromWorld for FxaaNode {
 | 
			
		||||
    fn from_world(world: &mut World) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            query: QueryState::new(world),
 | 
			
		||||
            cached_texture_bind_group: Mutex::new(None),
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    core_3d,
 | 
			
		||||
    fullscreen_vertex_shader::fullscreen_shader_vertex_state,
 | 
			
		||||
    prelude::Camera3d,
 | 
			
		||||
    prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
 | 
			
		||||
@ -18,7 +19,7 @@ use bevy_reflect::{Reflect, TypeUuid};
 | 
			
		||||
use bevy_render::{
 | 
			
		||||
    camera::{ExtractedCamera, TemporalJitter},
 | 
			
		||||
    prelude::{Camera, Projection},
 | 
			
		||||
    render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
 | 
			
		||||
    render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
 | 
			
		||||
    render_resource::{
 | 
			
		||||
        BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
 | 
			
		||||
        BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
 | 
			
		||||
@ -71,24 +72,17 @@ impl Plugin for TemporalAntiAliasPlugin {
 | 
			
		||||
                    prepare_taa_history_textures.in_set(RenderSet::Prepare),
 | 
			
		||||
                    prepare_taa_pipelines.in_set(RenderSet::Prepare),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_node::<TAANode>(core_3d::graph::NAME, draw_3d_graph::node::TAA)
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                &[
 | 
			
		||||
                    core_3d::graph::node::END_MAIN_PASS,
 | 
			
		||||
                    draw_3d_graph::node::TAA,
 | 
			
		||||
                    core_3d::graph::node::BLOOM,
 | 
			
		||||
                    core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        let taa_node = TAANode::new(&mut render_app.world);
 | 
			
		||||
        let mut graph = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
        let draw_3d_graph = graph
 | 
			
		||||
            .get_sub_graph_mut(crate::core_3d::graph::NAME)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        draw_3d_graph.add_node(draw_3d_graph::node::TAA, taa_node);
 | 
			
		||||
        // MAIN_PASS -> TAA -> BLOOM -> TONEMAPPING
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
            crate::core_3d::graph::node::END_MAIN_PASS,
 | 
			
		||||
            draw_3d_graph::node::TAA,
 | 
			
		||||
        );
 | 
			
		||||
        draw_3d_graph.add_node_edge(draw_3d_graph::node::TAA, crate::core_3d::graph::node::BLOOM);
 | 
			
		||||
        draw_3d_graph.add_node_edge(
 | 
			
		||||
            draw_3d_graph::node::TAA,
 | 
			
		||||
            crate::core_3d::graph::node::TONEMAPPING,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -168,8 +162,8 @@ struct TAANode {
 | 
			
		||||
    )>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TAANode {
 | 
			
		||||
    fn new(world: &mut World) -> Self {
 | 
			
		||||
impl FromWorld for TAANode {
 | 
			
		||||
    fn from_world(world: &mut World) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            view_query: QueryState::new(world),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										73
									
								
								crates/bevy_render/src/render_graph/app.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								crates/bevy_render/src/render_graph/app.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
use bevy_app::App;
 | 
			
		||||
use bevy_ecs::world::FromWorld;
 | 
			
		||||
 | 
			
		||||
use super::{Node, RenderGraph};
 | 
			
		||||
 | 
			
		||||
/// Adds common [`RenderGraph`] operations to [`App`].
 | 
			
		||||
pub trait RenderGraphApp {
 | 
			
		||||
    /// Add a [`Node`] to the [`RenderGraph`]:
 | 
			
		||||
    /// * Create the [`Node`] using the [`FromWorld`] implementation
 | 
			
		||||
    /// * Add it to the graph
 | 
			
		||||
    fn add_render_graph_node<T: Node + FromWorld>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sub_graph_name: &'static str,
 | 
			
		||||
        node_name: &'static str,
 | 
			
		||||
    ) -> &mut Self;
 | 
			
		||||
    /// Automatically add the required node edges based on the given ordering
 | 
			
		||||
    fn add_render_graph_edges(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sub_graph_name: &'static str,
 | 
			
		||||
        edges: &[&'static str],
 | 
			
		||||
    ) -> &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,
 | 
			
		||||
    ) -> &mut Self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RenderGraphApp for App {
 | 
			
		||||
    fn add_render_graph_node<T: Node + FromWorld>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sub_graph_name: &'static str,
 | 
			
		||||
        node_name: &'static str,
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let node = T::from_world(&mut self.world);
 | 
			
		||||
        let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
 | 
			
		||||
            "RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp",
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let graph = render_graph.sub_graph_mut(sub_graph_name);
 | 
			
		||||
        graph.add_node(node_name, node);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_render_graph_edges(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sub_graph_name: &'static str,
 | 
			
		||||
        edges: &[&'static str],
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
 | 
			
		||||
            "RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp",
 | 
			
		||||
        );
 | 
			
		||||
        let graph = render_graph.sub_graph_mut(sub_graph_name);
 | 
			
		||||
        graph.add_node_edges(edges);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_render_graph_edge(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        sub_graph_name: &'static str,
 | 
			
		||||
        output_edge: &'static str,
 | 
			
		||||
        input_edge: &'static str,
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let mut render_graph = self.world.get_resource_mut::<RenderGraph>().expect(
 | 
			
		||||
            "RenderGraph not found. Make sure you are using add_render_graph_node on the RenderApp",
 | 
			
		||||
        );
 | 
			
		||||
        let graph = render_graph.sub_graph_mut(sub_graph_name);
 | 
			
		||||
        graph.add_node_edge(output_edge, input_edge);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -119,6 +119,24 @@ impl RenderGraph {
 | 
			
		||||
        id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// 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) {
 | 
			
		||||
            let [a, b] = window else { break; };
 | 
			
		||||
            if let Err(err) = self.try_add_node_edge(*a, *b) {
 | 
			
		||||
                match err {
 | 
			
		||||
                    // Already existing edges are very easy to produce with this api
 | 
			
		||||
                    // and shouldn't cause a panic
 | 
			
		||||
                    RenderGraphError::EdgeAlreadyExists(_) => {}
 | 
			
		||||
                    _ => panic!("{err:?}"),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes the `node` with the `name` from the graph.
 | 
			
		||||
    /// If the name is does not exist, nothing happens.
 | 
			
		||||
    pub fn remove_node(
 | 
			
		||||
@ -583,6 +601,36 @@ impl RenderGraph {
 | 
			
		||||
    pub fn get_sub_graph_mut(&mut self, name: impl AsRef<str>) -> Option<&mut RenderGraph> {
 | 
			
		||||
        self.sub_graphs.get_mut(name.as_ref())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Retrieves the sub graph corresponding to the `name`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if any invalid node name is given.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # See also
 | 
			
		||||
    ///
 | 
			
		||||
    /// - [`get_sub_graph`](Self::get_sub_graph) for a fallible version.
 | 
			
		||||
    pub fn sub_graph(&self, name: impl AsRef<str>) -> &RenderGraph {
 | 
			
		||||
        self.sub_graphs
 | 
			
		||||
            .get(name.as_ref())
 | 
			
		||||
            .unwrap_or_else(|| panic!("Node {} not found in sub_graph", name.as_ref()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Retrieves the sub graph corresponding to the `name` mutably.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// Panics if any invalid node name 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<str>) -> &mut RenderGraph {
 | 
			
		||||
        self.sub_graphs
 | 
			
		||||
            .get_mut(name.as_ref())
 | 
			
		||||
            .unwrap_or_else(|| panic!("Node {} not found in sub_graph", name.as_ref()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Debug for RenderGraph {
 | 
			
		||||
@ -635,7 +683,7 @@ mod tests {
 | 
			
		||||
        },
 | 
			
		||||
        renderer::RenderContext,
 | 
			
		||||
    };
 | 
			
		||||
    use bevy_ecs::world::World;
 | 
			
		||||
    use bevy_ecs::world::{FromWorld, World};
 | 
			
		||||
    use bevy_utils::HashSet;
 | 
			
		||||
 | 
			
		||||
    #[derive(Debug)]
 | 
			
		||||
@ -676,6 +724,22 @@ mod tests {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
 | 
			
		||||
        graph
 | 
			
		||||
            .iter_node_inputs(name)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .map(|(_edge, node)| node.id)
 | 
			
		||||
            .collect::<HashSet<NodeId>>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn output_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
 | 
			
		||||
        graph
 | 
			
		||||
            .iter_node_outputs(name)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .map(|(_edge, node)| node.id)
 | 
			
		||||
            .collect::<HashSet<NodeId>>()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_graph_edges() {
 | 
			
		||||
        let mut graph = RenderGraph::default();
 | 
			
		||||
@ -688,22 +752,6 @@ mod tests {
 | 
			
		||||
        graph.add_node_edge("B", "C");
 | 
			
		||||
        graph.add_slot_edge("C", 0, "D", 0);
 | 
			
		||||
 | 
			
		||||
        fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
 | 
			
		||||
            graph
 | 
			
		||||
                .iter_node_inputs(name)
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .map(|(_edge, node)| node.id)
 | 
			
		||||
                .collect::<HashSet<NodeId>>()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn output_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
 | 
			
		||||
            graph
 | 
			
		||||
                .iter_node_outputs(name)
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .map(|(_edge, node)| node.id)
 | 
			
		||||
                .collect::<HashSet<NodeId>>()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert!(input_nodes("A", &graph).is_empty(), "A has no inputs");
 | 
			
		||||
        assert!(
 | 
			
		||||
            output_nodes("A", &graph) == HashSet::from_iter(vec![c_id]),
 | 
			
		||||
@ -803,4 +851,48 @@ mod tests {
 | 
			
		||||
            "Adding to a duplicate edge should return an error"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_add_node_edges() {
 | 
			
		||||
        struct SimpleNode;
 | 
			
		||||
        impl Node for SimpleNode {
 | 
			
		||||
            fn run(
 | 
			
		||||
                &self,
 | 
			
		||||
                _graph: &mut RenderGraphContext,
 | 
			
		||||
                _render_context: &mut RenderContext,
 | 
			
		||||
                _world: &World,
 | 
			
		||||
            ) -> Result<(), NodeRunError> {
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        impl FromWorld for SimpleNode {
 | 
			
		||||
            fn from_world(_world: &mut World) -> Self {
 | 
			
		||||
                Self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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_edges(&["A", "B", "C"]);
 | 
			
		||||
 | 
			
		||||
        assert!(
 | 
			
		||||
            output_nodes("A", &graph) == HashSet::from_iter(vec![b_id]),
 | 
			
		||||
            "A -> B"
 | 
			
		||||
        );
 | 
			
		||||
        assert!(
 | 
			
		||||
            input_nodes("B", &graph) == HashSet::from_iter(vec![a_id]),
 | 
			
		||||
            "A -> B"
 | 
			
		||||
        );
 | 
			
		||||
        assert!(
 | 
			
		||||
            output_nodes("B", &graph) == HashSet::from_iter(vec![c_id]),
 | 
			
		||||
            "B -> C"
 | 
			
		||||
        );
 | 
			
		||||
        assert!(
 | 
			
		||||
            input_nodes("C", &graph) == HashSet::from_iter(vec![b_id]),
 | 
			
		||||
            "B -> C"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
mod app;
 | 
			
		||||
mod context;
 | 
			
		||||
mod edge;
 | 
			
		||||
mod graph;
 | 
			
		||||
mod node;
 | 
			
		||||
mod node_slot;
 | 
			
		||||
 | 
			
		||||
pub use app::*;
 | 
			
		||||
pub use context::*;
 | 
			
		||||
pub use edge::*;
 | 
			
		||||
pub use graph::*;
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ use bevy::{
 | 
			
		||||
        extract_component::{
 | 
			
		||||
            ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
 | 
			
		||||
        },
 | 
			
		||||
        render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
 | 
			
		||||
        render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
 | 
			
		||||
        render_resource::{
 | 
			
		||||
            BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
 | 
			
		||||
            BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
 | 
			
		||||
@ -53,7 +53,8 @@ impl Plugin for PostProcessPlugin {
 | 
			
		||||
            // be extracted to the render world every frame.
 | 
			
		||||
            // This makes it possible to control the effect from the main world.
 | 
			
		||||
            // This plugin will take care of extracting it automatically.
 | 
			
		||||
            // It's important to derive [`ExtractComponent`] on [`PostProcessingSettings`] for this plugin to work correctly.
 | 
			
		||||
            // It's important to derive [`ExtractComponent`] on [`PostProcessingSettings`]
 | 
			
		||||
            // for this plugin to work correctly.
 | 
			
		||||
            .add_plugin(ExtractComponentPlugin::<PostProcessSettings>::default())
 | 
			
		||||
            // The settings will also be the data used in the shader.
 | 
			
		||||
            // This plugin will prepare the component for the GPU by creating a uniform buffer
 | 
			
		||||
@ -65,38 +66,35 @@ impl Plugin for PostProcessPlugin {
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Initialize the pipeline
 | 
			
		||||
        render_app.init_resource::<PostProcessPipeline>();
 | 
			
		||||
 | 
			
		||||
        // Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph.
 | 
			
		||||
        // It currently runs on each view/camera and executes each node in the specified order.
 | 
			
		||||
        // It will make sure that any node that needs a dependency from another node only runs when that dependency is done.
 | 
			
		||||
        //
 | 
			
		||||
        // Each node can execute arbitrary work, but it generally runs at least one render pass.
 | 
			
		||||
        // A node only has access to the render world, so if you need data from the main world
 | 
			
		||||
        // you need to extract it manually or with the plugin like above.
 | 
			
		||||
 | 
			
		||||
        // Create the node with the render world
 | 
			
		||||
        let node = PostProcessNode::new(&mut render_app.world);
 | 
			
		||||
 | 
			
		||||
        // Get the render graph for the entire app
 | 
			
		||||
        let mut graph = render_app.world.resource_mut::<RenderGraph>();
 | 
			
		||||
 | 
			
		||||
        // Get the render graph for 3d cameras/views
 | 
			
		||||
        let core_3d_graph = graph.get_sub_graph_mut(core_3d::graph::NAME).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Register the post process node in the 3d render graph
 | 
			
		||||
        core_3d_graph.add_node(PostProcessNode::NAME, node);
 | 
			
		||||
 | 
			
		||||
        // We now need to add an edge between our node and the nodes from bevy
 | 
			
		||||
        // to make sure our node is ordered correctly relative to other nodes.
 | 
			
		||||
        //
 | 
			
		||||
        // Here we want our effect to run after tonemapping and before the end of the main pass post processing
 | 
			
		||||
        core_3d_graph.add_node_edge(core_3d::graph::node::TONEMAPPING, PostProcessNode::NAME);
 | 
			
		||||
        core_3d_graph.add_node_edge(
 | 
			
		||||
            PostProcessNode::NAME,
 | 
			
		||||
            core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
 | 
			
		||||
        );
 | 
			
		||||
        render_app
 | 
			
		||||
            // Initialize the pipeline
 | 
			
		||||
            .init_resource::<PostProcessPipeline>()
 | 
			
		||||
            // Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph.
 | 
			
		||||
            // It currently runs on each view/camera and executes each node in the specified order.
 | 
			
		||||
            // It will make sure that any node that needs a dependency from another node
 | 
			
		||||
            // only runs when that dependency is done.
 | 
			
		||||
            //
 | 
			
		||||
            // Each node can execute arbitrary work, but it generally runs at least one render pass.
 | 
			
		||||
            // A node only has access to the render world, so if you need data from the main world
 | 
			
		||||
            // you need to extract it manually or with the plugin like above.
 | 
			
		||||
            // Add a [`Node`] to the [`RenderGraph`]
 | 
			
		||||
            // The Node needs to impl FromWorld
 | 
			
		||||
            .add_render_graph_node::<PostProcessNode>(
 | 
			
		||||
                // Specifiy 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,
 | 
			
		||||
            )
 | 
			
		||||
            .add_render_graph_edges(
 | 
			
		||||
                core_3d::graph::NAME,
 | 
			
		||||
                // 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,
 | 
			
		||||
                ],
 | 
			
		||||
            );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -109,8 +107,10 @@ struct PostProcessNode {
 | 
			
		||||
 | 
			
		||||
impl PostProcessNode {
 | 
			
		||||
    pub const NAME: &str = "post_process";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    fn new(world: &mut World) -> Self {
 | 
			
		||||
impl FromWorld for PostProcessNode {
 | 
			
		||||
    fn from_world(world: &mut World) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            query: QueryState::new(world),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user