mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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
12 changed files with 318 additions and 199 deletions
|
@ -16,7 +16,7 @@ use bevy_render::{
|
||||||
ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin,
|
ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin,
|
||||||
},
|
},
|
||||||
prelude::Color,
|
prelude::Color,
|
||||||
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderContext, RenderDevice},
|
renderer::{RenderContext, RenderDevice},
|
||||||
texture::{CachedTexture, TextureCache},
|
texture::{CachedTexture, TextureCache},
|
||||||
|
@ -71,45 +71,27 @@ impl Plugin for BloomPlugin {
|
||||||
prepare_upsampling_pipeline.in_set(RenderSet::Prepare),
|
prepare_upsampling_pipeline.in_set(RenderSet::Prepare),
|
||||||
queue_bloom_bind_groups.in_set(RenderSet::Queue),
|
queue_bloom_bind_groups.in_set(RenderSet::Queue),
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
|
|
||||||
// Add bloom to the 3d render graph
|
// Add bloom to the 3d render graph
|
||||||
{
|
.add_render_graph_node::<BloomNode>(core_3d::graph::NAME, core_3d::graph::node::BLOOM)
|
||||||
let bloom_node = BloomNode::new(&mut render_app.world);
|
.add_render_graph_edges(
|
||||||
let mut graph = render_app.world.resource_mut::<RenderGraph>();
|
core_3d::graph::NAME,
|
||||||
let draw_3d_graph = graph
|
&[
|
||||||
.get_sub_graph_mut(crate::core_3d::graph::NAME)
|
core_3d::graph::node::END_MAIN_PASS,
|
||||||
.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,
|
core_3d::graph::node::BLOOM,
|
||||||
);
|
core_3d::graph::node::TONEMAPPING,
|
||||||
draw_3d_graph.add_node_edge(
|
],
|
||||||
core_3d::graph::node::BLOOM,
|
)
|
||||||
crate::core_3d::graph::node::TONEMAPPING,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add bloom to the 2d render graph
|
// Add bloom to the 2d render graph
|
||||||
{
|
.add_render_graph_node::<BloomNode>(core_2d::graph::NAME, core_2d::graph::node::BLOOM)
|
||||||
let bloom_node = BloomNode::new(&mut render_app.world);
|
.add_render_graph_edges(
|
||||||
let mut graph = render_app.world.resource_mut::<RenderGraph>();
|
core_2d::graph::NAME,
|
||||||
let draw_2d_graph = graph
|
&[
|
||||||
.get_sub_graph_mut(crate::core_2d::graph::NAME)
|
core_2d::graph::node::MAIN_PASS,
|
||||||
.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,
|
core_2d::graph::node::BLOOM,
|
||||||
|
core_2d::graph::node::TONEMAPPING,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
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 {
|
impl FromWorld for BloomNode {
|
||||||
pub fn new(world: &mut World) -> Self {
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_query: QueryState::new(world),
|
view_query: QueryState::new(world),
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use bevy_reflect::{Reflect, TypeUuid};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
|
extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
|
||||||
prelude::Camera,
|
prelude::Camera,
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraphApp,
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
texture::BevyDefault,
|
texture::BevyDefault,
|
||||||
|
@ -114,49 +114,49 @@ impl Plugin for CASPlugin {
|
||||||
render_app
|
render_app
|
||||||
.init_resource::<CASPipeline>()
|
.init_resource::<CASPipeline>()
|
||||||
.init_resource::<SpecializedRenderPipelines<CASPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<CASPipeline>>()
|
||||||
.add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare));
|
.add_systems(Render, prepare_cas_pipelines.in_set(RenderSet::Prepare))
|
||||||
{
|
// 3d
|
||||||
let cas_node = CASNode::new(&mut render_app.world);
|
.add_render_graph_node::<CASNode>(
|
||||||
let mut binding = render_app.world.resource_mut::<RenderGraph>();
|
core_3d::graph::NAME,
|
||||||
let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap();
|
core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
||||||
|
)
|
||||||
graph.add_node(core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING, cas_node);
|
.add_render_graph_edge(
|
||||||
|
core_3d::graph::NAME,
|
||||||
graph.add_node_edge(
|
|
||||||
core_3d::graph::node::TONEMAPPING,
|
core_3d::graph::node::TONEMAPPING,
|
||||||
core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
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::FXAA,
|
||||||
core_3d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
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::CONTRAST_ADAPTIVE_SHARPENING,
|
||||||
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
);
|
)
|
||||||
}
|
// 2d
|
||||||
{
|
.add_render_graph_node::<CASNode>(
|
||||||
let cas_node = CASNode::new(&mut render_app.world);
|
core_2d::graph::NAME,
|
||||||
let mut binding = render_app.world.resource_mut::<RenderGraph>();
|
core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
||||||
let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap();
|
)
|
||||||
|
.add_render_graph_edge(
|
||||||
graph.add_node(core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING, cas_node);
|
core_2d::graph::NAME,
|
||||||
|
|
||||||
graph.add_node_edge(
|
|
||||||
core_2d::graph::node::TONEMAPPING,
|
core_2d::graph::node::TONEMAPPING,
|
||||||
core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
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::FXAA,
|
||||||
core_2d::graph::node::CONTRAST_ADAPTIVE_SHARPENING,
|
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::CONTRAST_ADAPTIVE_SHARPENING,
|
||||||
core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct CASPipeline {
|
pub struct CASPipeline {
|
||||||
|
|
|
@ -28,8 +28,8 @@ pub struct CASNode {
|
||||||
cached_bind_group: Mutex<Option<(BufferId, TextureViewId, BindGroup)>>,
|
cached_bind_group: Mutex<Option<(BufferId, TextureViewId, BindGroup)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CASNode {
|
impl FromWorld for CASNode {
|
||||||
pub fn new(world: &mut World) -> Self {
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
query: QueryState::new(world),
|
query: QueryState::new(world),
|
||||||
cached_bind_group: Mutex::new(None),
|
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::TONEMAPPING, tonemapping);
|
||||||
draw_2d_graph.add_node(graph::node::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
|
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(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::TONEMAPPING,
|
||||||
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
);
|
|
||||||
draw_2d_graph.add_node_edge(
|
|
||||||
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
|
||||||
graph::node::UPSCALING,
|
graph::node::UPSCALING,
|
||||||
);
|
]);
|
||||||
|
|
||||||
graph.add_sub_graph(graph::NAME, draw_2d_graph);
|
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::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
|
||||||
draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);
|
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_edges(&[
|
||||||
draw_3d_graph.add_node_edge(graph::node::START_MAIN_PASS, graph::node::MAIN_OPAQUE_PASS);
|
graph::node::PREPASS,
|
||||||
draw_3d_graph.add_node_edge(
|
graph::node::START_MAIN_PASS,
|
||||||
graph::node::MAIN_OPAQUE_PASS,
|
graph::node::MAIN_OPAQUE_PASS,
|
||||||
graph::node::MAIN_TRANSPARENT_PASS,
|
graph::node::MAIN_TRANSPARENT_PASS,
|
||||||
);
|
|
||||||
draw_3d_graph.add_node_edge(
|
|
||||||
graph::node::MAIN_TRANSPARENT_PASS,
|
|
||||||
graph::node::END_MAIN_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::TONEMAPPING,
|
||||||
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
);
|
|
||||||
draw_3d_graph.add_node_edge(
|
|
||||||
graph::node::END_MAIN_PASS_POST_PROCESSING,
|
|
||||||
graph::node::UPSCALING,
|
graph::node::UPSCALING,
|
||||||
);
|
]);
|
||||||
|
|
||||||
graph.add_sub_graph(graph::NAME, draw_3d_graph);
|
graph.add_sub_graph(graph::NAME, draw_3d_graph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use bevy_reflect::{
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||||
prelude::Camera,
|
prelude::Camera,
|
||||||
render_graph::RenderGraph,
|
render_graph::RenderGraphApp,
|
||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
texture::BevyDefault,
|
texture::BevyDefault,
|
||||||
|
@ -90,42 +90,27 @@ impl Plugin for FxaaPlugin {
|
||||||
render_app
|
render_app
|
||||||
.init_resource::<FxaaPipeline>()
|
.init_resource::<FxaaPipeline>()
|
||||||
.init_resource::<SpecializedRenderPipelines<FxaaPipeline>>()
|
.init_resource::<SpecializedRenderPipelines<FxaaPipeline>>()
|
||||||
.add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare));
|
.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(
|
||||||
let fxaa_node = FxaaNode::new(&mut render_app.world);
|
core_3d::graph::NAME,
|
||||||
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::TONEMAPPING,
|
||||||
core_3d::graph::node::FXAA,
|
core_3d::graph::node::FXAA,
|
||||||
);
|
|
||||||
graph.add_node_edge(
|
|
||||||
core_3d::graph::node::FXAA,
|
|
||||||
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
);
|
],
|
||||||
}
|
)
|
||||||
{
|
.add_render_graph_node::<FxaaNode>(core_2d::graph::NAME, core_2d::graph::node::FXAA)
|
||||||
let fxaa_node = FxaaNode::new(&mut render_app.world);
|
.add_render_graph_edges(
|
||||||
let mut binding = render_app.world.resource_mut::<RenderGraph>();
|
core_2d::graph::NAME,
|
||||||
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::TONEMAPPING,
|
||||||
core_2d::graph::node::FXAA,
|
core_2d::graph::node::FXAA,
|
||||||
);
|
|
||||||
graph.add_node_edge(
|
|
||||||
core_2d::graph::node::FXAA,
|
|
||||||
core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Resource, Deref)]
|
#[derive(Resource, Deref)]
|
||||||
pub struct FxaaPipeline {
|
pub struct FxaaPipeline {
|
||||||
|
|
|
@ -27,8 +27,8 @@ pub struct FxaaNode {
|
||||||
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FxaaNode {
|
impl FromWorld for FxaaNode {
|
||||||
pub fn new(world: &mut World) -> Self {
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
query: QueryState::new(world),
|
query: QueryState::new(world),
|
||||||
cached_texture_bind_group: Mutex::new(None),
|
cached_texture_bind_group: Mutex::new(None),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
core_3d,
|
||||||
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
|
fullscreen_vertex_shader::fullscreen_shader_vertex_state,
|
||||||
prelude::Camera3d,
|
prelude::Camera3d,
|
||||||
prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
|
prepass::{DepthPrepass, MotionVectorPrepass, ViewPrepassTextures},
|
||||||
|
@ -18,7 +19,7 @@ use bevy_reflect::{Reflect, TypeUuid};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
camera::{ExtractedCamera, TemporalJitter},
|
camera::{ExtractedCamera, TemporalJitter},
|
||||||
prelude::{Camera, Projection},
|
prelude::{Camera, Projection},
|
||||||
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
|
||||||
render_resource::{
|
render_resource::{
|
||||||
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
||||||
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
|
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
|
||||||
|
@ -71,23 +72,16 @@ impl Plugin for TemporalAntiAliasPlugin {
|
||||||
prepare_taa_history_textures.in_set(RenderSet::Prepare),
|
prepare_taa_history_textures.in_set(RenderSet::Prepare),
|
||||||
prepare_taa_pipelines.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)
|
||||||
let taa_node = TAANode::new(&mut render_app.world);
|
.add_render_graph_edges(
|
||||||
let mut graph = render_app.world.resource_mut::<RenderGraph>();
|
core_3d::graph::NAME,
|
||||||
let draw_3d_graph = graph
|
&[
|
||||||
.get_sub_graph_mut(crate::core_3d::graph::NAME)
|
core_3d::graph::node::END_MAIN_PASS,
|
||||||
.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::node::TAA,
|
||||||
);
|
core_3d::graph::node::BLOOM,
|
||||||
draw_3d_graph.add_node_edge(draw_3d_graph::node::TAA, crate::core_3d::graph::node::BLOOM);
|
core_3d::graph::node::TONEMAPPING,
|
||||||
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 {
|
impl FromWorld for TAANode {
|
||||||
fn new(world: &mut World) -> Self {
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
view_query: QueryState::new(world),
|
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
|
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.
|
/// Removes the `node` with the `name` from the graph.
|
||||||
/// If the name is does not exist, nothing happens.
|
/// If the name is does not exist, nothing happens.
|
||||||
pub fn remove_node(
|
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> {
|
pub fn get_sub_graph_mut(&mut self, name: impl AsRef<str>) -> Option<&mut RenderGraph> {
|
||||||
self.sub_graphs.get_mut(name.as_ref())
|
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 {
|
impl Debug for RenderGraph {
|
||||||
|
@ -635,7 +683,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
renderer::RenderContext,
|
renderer::RenderContext,
|
||||||
};
|
};
|
||||||
use bevy_ecs::world::World;
|
use bevy_ecs::world::{FromWorld, World};
|
||||||
use bevy_utils::HashSet;
|
use bevy_utils::HashSet;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -676,18 +724,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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);
|
|
||||||
|
|
||||||
fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
|
fn input_nodes(name: &'static str, graph: &RenderGraph) -> HashSet<NodeId> {
|
||||||
graph
|
graph
|
||||||
.iter_node_inputs(name)
|
.iter_node_inputs(name)
|
||||||
|
@ -704,6 +740,18 @@ mod tests {
|
||||||
.collect::<HashSet<NodeId>>()
|
.collect::<HashSet<NodeId>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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");
|
assert!(input_nodes("A", &graph).is_empty(), "A has no inputs");
|
||||||
assert!(
|
assert!(
|
||||||
output_nodes("A", &graph) == HashSet::from_iter(vec![c_id]),
|
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"
|
"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 context;
|
||||||
mod edge;
|
mod edge;
|
||||||
mod graph;
|
mod graph;
|
||||||
mod node;
|
mod node;
|
||||||
mod node_slot;
|
mod node_slot;
|
||||||
|
|
||||||
|
pub use app::*;
|
||||||
pub use context::*;
|
pub use context::*;
|
||||||
pub use edge::*;
|
pub use edge::*;
|
||||||
pub use graph::*;
|
pub use graph::*;
|
||||||
|
|
|
@ -15,7 +15,7 @@ use bevy::{
|
||||||
extract_component::{
|
extract_component::{
|
||||||
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
|
||||||
},
|
},
|
||||||
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
|
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
|
||||||
render_resource::{
|
render_resource::{
|
||||||
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
|
||||||
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
|
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
|
||||||
|
@ -53,7 +53,8 @@ impl Plugin for PostProcessPlugin {
|
||||||
// be extracted to the render world every frame.
|
// be extracted to the render world every frame.
|
||||||
// This makes it possible to control the effect from the main world.
|
// This makes it possible to control the effect from the main world.
|
||||||
// This plugin will take care of extracting it automatically.
|
// 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())
|
.add_plugin(ExtractComponentPlugin::<PostProcessSettings>::default())
|
||||||
// The settings will also be the data used in the shader.
|
// 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
|
// This plugin will prepare the component for the GPU by creating a uniform buffer
|
||||||
|
@ -65,37 +66,34 @@ impl Plugin for PostProcessPlugin {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render_app
|
||||||
// Initialize the pipeline
|
// Initialize the pipeline
|
||||||
render_app.init_resource::<PostProcessPipeline>();
|
.init_resource::<PostProcessPipeline>()
|
||||||
|
|
||||||
// Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph.
|
// 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 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.
|
// 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.
|
// 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
|
// 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.
|
// you need to extract it manually or with the plugin like above.
|
||||||
|
// Add a [`Node`] to the [`RenderGraph`]
|
||||||
// Create the node with the render world
|
// The Node needs to impl FromWorld
|
||||||
let node = PostProcessNode::new(&mut render_app.world);
|
.add_render_graph_node::<PostProcessNode>(
|
||||||
|
// Specifiy the name of the graph, in this case we want the graph for 3d
|
||||||
// Get the render graph for the entire app
|
core_3d::graph::NAME,
|
||||||
let mut graph = render_app.world.resource_mut::<RenderGraph>();
|
// It also needs the name of the node
|
||||||
|
PostProcessNode::NAME,
|
||||||
// Get the render graph for 3d cameras/views
|
)
|
||||||
let core_3d_graph = graph.get_sub_graph_mut(core_3d::graph::NAME).unwrap();
|
.add_render_graph_edges(
|
||||||
|
core_3d::graph::NAME,
|
||||||
// Register the post process node in the 3d render graph
|
// Specify the node ordering.
|
||||||
core_3d_graph.add_node(PostProcessNode::NAME, node);
|
// This will automatically create all required node edges to enforce the given ordering.
|
||||||
|
&[
|
||||||
// We now need to add an edge between our node and the nodes from bevy
|
core_3d::graph::node::TONEMAPPING,
|
||||||
// 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,
|
PostProcessNode::NAME,
|
||||||
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,8 +107,10 @@ struct PostProcessNode {
|
||||||
|
|
||||||
impl PostProcessNode {
|
impl PostProcessNode {
|
||||||
pub const NAME: &str = "post_process";
|
pub const NAME: &str = "post_process";
|
||||||
|
}
|
||||||
|
|
||||||
fn new(world: &mut World) -> Self {
|
impl FromWorld for PostProcessNode {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
query: QueryState::new(world),
|
query: QueryState::new(world),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue