Make render graph slots optional for most cases (#8109)

# Objective

- Currently, the render graph slots are only used to pass the
view_entity around. This introduces significant boilerplate for very
little value. Instead of using slots for this, make the view_entity part
of the `RenderGraphContext`. This also means we won't need to have
`IN_VIEW` on every node and and we'll be able to use the default impl of
`Node::input()`.

## Solution

- Add `view_entity: Option<Entity>` to the `RenderGraphContext`
- Update all nodes to use this instead of entity slot input

---

## Changelog

- Add optional `view_entity` to `RenderGraphContext`

## Migration Guide

You can now get the view_entity directly from the `RenderGraphContext`. 

When implementing the Node:

```rust
// 0.10
struct FooNode;
impl FooNode {
    const IN_VIEW: &'static str = "view";
}
impl Node for FooNode {
    fn input(&self) -> Vec<SlotInfo> {
        vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
    }
    fn run(
        &self,
        graph: &mut RenderGraphContext,
        // ... 
    ) -> Result<(), NodeRunError> {
        let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
        // ...
        Ok(())
    }
}

// 0.11
struct FooNode;
impl Node for FooNode {
    fn run(
        &self,
        graph: &mut RenderGraphContext,
        // ... 
    ) -> Result<(), NodeRunError> {
        let view_entity = graph.view_entity();
        // ...
        Ok(())
    }
}
```

When adding the node to the graph, you don't need to specify a slot_edge
for the view_entity.

```rust
// 0.10
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
    graph::input::VIEW_ENTITY,
    SlotType::Entity,
)]);
graph.add_slot_edge(
    input_node_id,
    graph::input::VIEW_ENTITY,
    FooNode::NAME,
    FooNode::IN_VIEW,
);
// add_node_edge ...

// 0.11
let mut graph = RenderGraph::default();
graph.add_node(FooNode::NAME, node);
// add_node_edge ...
```

## Notes

This PR paired with #8007 will help reduce a lot of annoying boilerplate
with the render nodes. Depending on which one gets merged first. It will
require a bit of clean up work to make both compatible.

I tagged this as a breaking change, because using the old system to get
the view_entity will break things because it's not a node input slot
anymore.

## Notes for reviewers

A lot of the diffs are just removing the slots in every nodes and graph
creation. The important part is mostly in the
graph_runner/CameraDriverNode.
This commit is contained in:
IceSentry 2023-03-21 16:11:13 -04:00 committed by GitHub
parent 353f2e0b37
commit 2c21d423fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 63 additions and 239 deletions

View file

@ -16,7 +16,7 @@ use bevy_render::{
ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin,
},
prelude::Color,
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
render_resource::*,
renderer::{RenderContext, RenderDevice},
texture::{CachedTexture, TextureCache},
@ -83,12 +83,6 @@ impl Plugin for BloomPlugin {
.get_sub_graph_mut(crate::core_3d::graph::NAME)
.unwrap();
draw_3d_graph.add_node(core_3d::graph::node::BLOOM, bloom_node);
draw_3d_graph.add_slot_edge(
draw_3d_graph.input_node().id,
crate::core_3d::graph::input::VIEW_ENTITY,
core_3d::graph::node::BLOOM,
BloomNode::IN_VIEW,
);
// MAIN_PASS -> BLOOM -> TONEMAPPING
draw_3d_graph.add_node_edge(
crate::core_3d::graph::node::MAIN_PASS,
@ -108,12 +102,6 @@ impl Plugin for BloomPlugin {
.get_sub_graph_mut(crate::core_2d::graph::NAME)
.unwrap();
draw_2d_graph.add_node(core_2d::graph::node::BLOOM, bloom_node);
draw_2d_graph.add_slot_edge(
draw_2d_graph.input_node().id,
crate::core_2d::graph::input::VIEW_ENTITY,
core_2d::graph::node::BLOOM,
BloomNode::IN_VIEW,
);
// MAIN_PASS -> BLOOM -> TONEMAPPING
draw_2d_graph.add_node_edge(
crate::core_2d::graph::node::MAIN_PASS,
@ -141,8 +129,6 @@ pub struct BloomNode {
}
impl BloomNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
view_query: QueryState::new(world),
@ -151,10 +137,6 @@ impl BloomNode {
}
impl Node for BloomNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.view_query.update_archetypes(world);
}
@ -174,7 +156,7 @@ impl Node for BloomNode {
let downsampling_pipeline_res = world.resource::<BloomDownsamplingPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();
let uniforms = world.resource::<ComponentUniforms<BloomUniforms>>();
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let Ok((
camera,
view_target,

View file

@ -5,7 +5,7 @@ use crate::{
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDescriptor},
renderer::RenderContext,
@ -27,8 +27,6 @@ pub struct MainPass2dNode {
}
impl MainPass2dNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
query: world.query_filtered(),
@ -37,10 +35,6 @@ impl MainPass2dNode {
}
impl Node for MainPass2dNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(MainPass2dNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -51,7 +45,7 @@ impl Node for MainPass2dNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let (camera, transparent_phase, target, camera_2d) =
if let Ok(result) = self.query.get_manual(world, view_entity) {
result

View file

@ -25,7 +25,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{
camera::Camera,
extract_component::ExtractComponentPlugin,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_graph::{EmptyNode, RenderGraph},
render_phase::{
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
@ -73,28 +73,6 @@ 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);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
)]);
draw_2d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::MAIN_PASS,
MainPass2dNode::IN_VIEW,
);
draw_2d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::TONEMAPPING,
TonemappingNode::IN_VIEW,
);
draw_2d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
);
draw_2d_graph.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING);
draw_2d_graph.add_node_edge(
graph::node::TONEMAPPING,

View file

@ -6,7 +6,7 @@ use crate::{
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
renderer::RenderContext,
@ -35,8 +35,6 @@ pub struct MainPass3dNode {
}
impl MainPass3dNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
query: world.query_filtered(),
@ -45,10 +43,6 @@ impl MainPass3dNode {
}
impl Node for MainPass3dNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(MainPass3dNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -59,7 +53,7 @@ impl Node for MainPass3dNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let Ok((
camera,
opaque_phase,

View file

@ -29,7 +29,7 @@ use bevy_render::{
camera::{Camera, ExtractedCamera},
extract_component::ExtractComponentPlugin,
prelude::Msaa,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_graph::{EmptyNode, RenderGraph},
render_phase::{
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
RenderPhase,
@ -94,34 +94,6 @@ 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);
let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
)]);
draw_3d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::PREPASS,
PrepassNode::IN_VIEW,
);
draw_3d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::MAIN_PASS,
MainPass3dNode::IN_VIEW,
);
draw_3d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::TONEMAPPING,
TonemappingNode::IN_VIEW,
);
draw_3d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
);
draw_3d_graph.add_node_edge(graph::node::PREPASS, graph::node::MAIN_PASS);
draw_3d_graph.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING);
draw_3d_graph.add_node_edge(

View file

@ -99,13 +99,6 @@ impl Plugin for FxaaPlugin {
graph.add_node(core_3d::graph::node::FXAA, fxaa_node);
graph.add_slot_edge(
graph.input_node().id,
core_3d::graph::input::VIEW_ENTITY,
core_3d::graph::node::FXAA,
FxaaNode::IN_VIEW,
);
graph.add_node_edge(
core_3d::graph::node::TONEMAPPING,
core_3d::graph::node::FXAA,
@ -122,13 +115,6 @@ impl Plugin for FxaaPlugin {
graph.add_node(core_2d::graph::node::FXAA, fxaa_node);
graph.add_slot_edge(
graph.input_node().id,
core_2d::graph::input::VIEW_ENTITY,
core_2d::graph::node::FXAA,
FxaaNode::IN_VIEW,
);
graph.add_node_edge(
core_2d::graph::node::TONEMAPPING,
core_2d::graph::node::FXAA,

View file

@ -4,7 +4,7 @@ use crate::fxaa::{CameraFxaaPipeline, Fxaa, FxaaPipeline};
use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryState;
use bevy_render::{
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode, Operations,
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
@ -28,8 +28,6 @@ pub struct FxaaNode {
}
impl FxaaNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
query: QueryState::new(world),
@ -39,10 +37,6 @@ impl FxaaNode {
}
impl Node for FxaaNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(FxaaNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -53,7 +47,7 @@ impl Node for FxaaNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let pipeline_cache = world.resource::<PipelineCache>();
let fxaa_pipeline = world.resource::<FxaaPipeline>();

View file

@ -3,7 +3,7 @@ use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
renderer::RenderContext,
view::{Msaa, ViewTarget},
Render, RenderSet,
@ -28,7 +28,6 @@ impl Plugin for MsaaWritebackPlugin {
let msaa_writeback_3d = MsaaWritebackNode::new(&mut render_app.world);
let mut graph = render_app.world.resource_mut::<RenderGraph>();
if let Some(core_2d) = graph.get_sub_graph_mut(crate::core_2d::graph::NAME) {
let input_node = core_2d.input_node().id;
core_2d.add_node(
crate::core_2d::graph::node::MSAA_WRITEBACK,
msaa_writeback_2d,
@ -37,16 +36,9 @@ impl Plugin for MsaaWritebackPlugin {
crate::core_2d::graph::node::MSAA_WRITEBACK,
crate::core_2d::graph::node::MAIN_PASS,
);
core_2d.add_slot_edge(
input_node,
crate::core_2d::graph::input::VIEW_ENTITY,
crate::core_2d::graph::node::MSAA_WRITEBACK,
MsaaWritebackNode::IN_VIEW,
);
}
if let Some(core_3d) = graph.get_sub_graph_mut(crate::core_3d::graph::NAME) {
let input_node = core_3d.input_node().id;
core_3d.add_node(
crate::core_3d::graph::node::MSAA_WRITEBACK,
msaa_writeback_3d,
@ -55,12 +47,6 @@ impl Plugin for MsaaWritebackPlugin {
crate::core_3d::graph::node::MSAA_WRITEBACK,
crate::core_3d::graph::node::MAIN_PASS,
);
core_3d.add_slot_edge(
input_node,
crate::core_3d::graph::input::VIEW_ENTITY,
crate::core_3d::graph::node::MSAA_WRITEBACK,
MsaaWritebackNode::IN_VIEW,
);
}
}
}
@ -70,8 +56,6 @@ pub struct MsaaWritebackNode {
}
impl MsaaWritebackNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
cameras: world.query(),
@ -80,9 +64,6 @@ impl MsaaWritebackNode {
}
impl Node for MsaaWritebackNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.cameras.update_archetypes(world);
}
@ -92,7 +73,7 @@ impl Node for MsaaWritebackNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
if let Ok((target, blit_pipeline_id)) = self.cameras.get_manual(world, view_entity) {
let blit_pipeline = world.resource::<BlitPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();

View file

@ -3,7 +3,7 @@ use bevy_ecs::query::QueryState;
use bevy_render::{
camera::ExtractedCamera,
prelude::Color,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::RenderPhase,
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
@ -34,8 +34,6 @@ pub struct PrepassNode {
}
impl PrepassNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
main_view_query: QueryState::new(world),
@ -44,10 +42,6 @@ impl PrepassNode {
}
impl Node for PrepassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.main_view_query.update_archetypes(world);
}
@ -58,7 +52,7 @@ impl Node for PrepassNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let Ok((
camera,
opaque_prepass_phase,

View file

@ -6,7 +6,7 @@ use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryState;
use bevy_render::{
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferId, LoadOp,
Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
@ -34,8 +34,6 @@ pub struct TonemappingNode {
}
impl TonemappingNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
query: QueryState::new(world),
@ -46,10 +44,6 @@ impl TonemappingNode {
}
impl Node for TonemappingNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(TonemappingNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -60,7 +54,7 @@ impl Node for TonemappingNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let pipeline_cache = world.resource::<PipelineCache>();
let tonemapping_pipeline = world.resource::<TonemappingPipeline>();
let gpu_images = world.get_resource::<RenderAssets<Image>>().unwrap();

View file

@ -3,7 +3,7 @@ use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryState;
use bevy_render::{
camera::{CameraOutputMode, ExtractedCamera},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_resource::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, LoadOp, Operations,
PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor,
@ -27,8 +27,6 @@ pub struct UpscalingNode {
}
impl UpscalingNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
query: QueryState::new(world),
@ -38,10 +36,6 @@ impl UpscalingNode {
}
impl Node for UpscalingNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(UpscalingNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
@ -52,7 +46,7 @@ impl Node for UpscalingNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
let pipeline_cache = world.get_resource::<PipelineCache>().unwrap();
let blit_pipeline = world.get_resource::<BlitPipeline>().unwrap();

View file

@ -294,11 +294,5 @@ impl Plugin for PbrPlugin {
draw_3d_graph::node::SHADOW_PASS,
bevy_core_pipeline::core_3d::graph::node::MAIN_PASS,
);
draw_3d_graph.add_slot_edge(
draw_3d_graph.input_node().id,
bevy_core_pipeline::core_3d::graph::input::VIEW_ENTITY,
draw_3d_graph::node::SHADOW_PASS,
ShadowPassNode::IN_VIEW,
);
}
}

View file

@ -15,7 +15,7 @@ use bevy_render::{
color::Color,
mesh::Mesh,
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraphContext},
render_phase::{
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
},
@ -1697,8 +1697,6 @@ pub struct ShadowPassNode {
}
impl ShadowPassNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
main_view_query: QueryState::new(world),
@ -1708,10 +1706,6 @@ impl ShadowPassNode {
}
impl Node for ShadowPassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(ShadowPassNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.main_view_query.update_archetypes(world);
self.view_light_query.update_archetypes(world);
@ -1723,7 +1717,7 @@ impl Node for ShadowPassNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let view_entity = graph.view_entity();
if let Ok(view_lights) = self.main_view_query.get_manual(world, view_entity) {
for view_light_entity in view_lights.lights.iter().copied() {
let (view_light, shadow_phase) = self

View file

@ -1,6 +1,6 @@
use crate::{
camera::{ExtractedCamera, NormalizedRenderTarget, SortedCameras},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue},
render_graph::{Node, NodeRunError, RenderGraphContext},
renderer::RenderContext,
view::ExtractedWindows,
};
@ -39,7 +39,8 @@ impl Node for CameraDriverNode {
}
graph.run_sub_graph(
camera.render_graph.clone(),
vec![SlotValue::Entity(sorted_camera.entity)],
vec![],
Some(sorted_camera.entity),
)?;
}
}

View file

@ -11,6 +11,7 @@ use thiserror::Error;
pub struct RunSubGraph {
pub name: Cow<'static, str>,
pub inputs: Vec<SlotValue>,
pub view_entity: Option<Entity>,
}
/// The context with all graph information required to run a [`Node`](super::Node).
@ -27,6 +28,11 @@ pub struct RenderGraphContext<'a> {
inputs: &'a [SlotValue],
outputs: &'a mut [Option<SlotValue>],
run_sub_graphs: Vec<RunSubGraph>,
/// The view_entity associated with the render graph being executed
/// This is optional because you aren't required to have a view_entity for a node.
/// For example, compute shader nodes don't have one.
/// It should always be set when the RenderGraph is running on a View.
view_entity: Option<Entity>,
}
impl<'a> RenderGraphContext<'a> {
@ -43,6 +49,7 @@ impl<'a> RenderGraphContext<'a> {
inputs,
outputs,
run_sub_graphs: Vec::new(),
view_entity: None,
}
}
@ -158,11 +165,24 @@ impl<'a> RenderGraphContext<'a> {
Ok(())
}
pub fn view_entity(&self) -> Entity {
self.view_entity.unwrap()
}
pub fn get_view_entity(&self) -> Option<Entity> {
self.view_entity
}
pub fn set_view_entity(&mut self, view_entity: Entity) {
self.view_entity = Some(view_entity);
}
/// Queues up a sub graph for execution after the node has finished running.
pub fn run_sub_graph(
&mut self,
name: impl Into<Cow<'static, str>>,
inputs: Vec<SlotValue>,
view_entity: Option<Entity>,
) -> Result<(), RunSubGraphError> {
let name = name.into();
let sub_graph = self
@ -193,7 +213,11 @@ impl<'a> RenderGraphContext<'a> {
return Err(RunSubGraphError::SubGraphHasNoInputs(name));
}
self.run_sub_graphs.push(RunSubGraph { name, inputs });
self.run_sub_graphs.push(RunSubGraph {
name,
inputs,
view_entity,
});
Ok(())
}

View file

@ -2,7 +2,7 @@ use crate::{
define_atomic_id,
render_graph::{
Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError,
RunSubGraphError, SlotInfo, SlotInfos, SlotType, SlotValue,
RunSubGraphError, SlotInfo, SlotInfos,
},
renderer::RenderContext,
};
@ -306,14 +306,13 @@ impl Node for EmptyNode {
}
}
/// A [`RenderGraph`](super::RenderGraph) [`Node`] that takes a view entity as input and runs the configured graph name once.
/// A [`RenderGraph`](super::RenderGraph) [`Node`] that runs the configured graph name once.
/// This makes it easier to insert sub-graph runs into a graph.
pub struct RunGraphOnViewNode {
graph_name: Cow<'static, str>,
}
impl RunGraphOnViewNode {
pub const IN_VIEW: &'static str = "view";
pub fn new<T: Into<Cow<'static, str>>>(graph_name: T) -> Self {
Self {
graph_name: graph_name.into(),
@ -322,20 +321,13 @@ impl RunGraphOnViewNode {
}
impl Node for RunGraphOnViewNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(Self::IN_VIEW, SlotType::Entity)]
}
fn run(
&self,
graph: &mut RenderGraphContext,
_render_context: &mut RenderContext,
_world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
graph.run_sub_graph(
self.graph_name.clone(),
vec![SlotValue::Entity(view_entity)],
)?;
graph.run_sub_graph(self.graph_name.clone(), vec![], Some(graph.view_entity()))?;
Ok(())
}
}

View file

@ -1,4 +1,4 @@
use bevy_ecs::world::World;
use bevy_ecs::{prelude::Entity, world::World};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use bevy_utils::HashMap;
@ -59,7 +59,7 @@ impl RenderGraphRunner {
world: &World,
) -> Result<(), RenderGraphRunnerError> {
let mut render_context = RenderContext::new(render_device);
Self::run_graph(graph, None, &mut render_context, world, &[])?;
Self::run_graph(graph, None, &mut render_context, world, &[], None)?;
{
#[cfg(feature = "trace")]
let _span = info_span!("submit_graph_commands").entered();
@ -74,6 +74,7 @@ impl RenderGraphRunner {
render_context: &mut RenderContext,
world: &World,
inputs: &[SlotValue],
view_entity: Option<Entity>,
) -> Result<(), RenderGraphRunnerError> {
let mut node_outputs: HashMap<NodeId, SmallVec<[SlotValue; 4]>> = HashMap::default();
#[cfg(feature = "trace")]
@ -175,6 +176,10 @@ impl RenderGraphRunner {
smallvec![None; node_state.output_slots.len()];
{
let mut context = RenderGraphContext::new(graph, node_state, &inputs, &mut outputs);
if let Some(view_entity) = view_entity {
context.set_view_entity(view_entity);
}
{
#[cfg(feature = "trace")]
let _span = info_span!("node", name = node_state.type_name).entered();
@ -192,6 +197,7 @@ impl RenderGraphRunner {
render_context,
world,
&run_sub_graph.inputs,
run_sub_graph.view_entity,
)?;
}
}

View file

@ -19,7 +19,7 @@ use bevy_render::{
camera::Camera,
color::Color,
render_asset::RenderAssets,
render_graph::{RenderGraph, RunGraphOnViewNode, SlotInfo, SlotType},
render_graph::{RenderGraph, RunGraphOnViewNode},
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions, RenderPhase},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
@ -44,9 +44,6 @@ pub mod node {
pub mod draw_ui_graph {
pub const NAME: &str = "draw_ui";
pub mod input {
pub const VIEW_ENTITY: &str = "view_entity";
}
pub mod node {
pub const UI_PASS: &str = "ui_pass";
}
@ -110,12 +107,6 @@ pub fn build_ui_render(app: &mut App) {
bevy_core_pipeline::core_2d::graph::node::MAIN_PASS,
draw_ui_graph::node::UI_PASS,
);
graph_2d.add_slot_edge(
graph_2d.input_node().id,
bevy_core_pipeline::core_2d::graph::input::VIEW_ENTITY,
draw_ui_graph::node::UI_PASS,
RunGraphOnViewNode::IN_VIEW,
);
graph_2d.add_node_edge(
bevy_core_pipeline::core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING,
draw_ui_graph::node::UI_PASS,
@ -144,12 +135,6 @@ pub fn build_ui_render(app: &mut App) {
draw_ui_graph::node::UI_PASS,
bevy_core_pipeline::core_3d::graph::node::UPSCALING,
);
graph_3d.add_slot_edge(
graph_3d.input_node().id,
bevy_core_pipeline::core_3d::graph::input::VIEW_ENTITY,
draw_ui_graph::node::UI_PASS,
RunGraphOnViewNode::IN_VIEW,
);
}
}
@ -157,16 +142,6 @@ 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);
let input_node_id = ui_graph.set_input(vec![SlotInfo::new(
draw_ui_graph::input::VIEW_ENTITY,
SlotType::Entity,
)]);
ui_graph.add_slot_edge(
input_node_id,
draw_ui_graph::input::VIEW_ENTITY,
draw_ui_graph::node::UI_PASS,
UiPassNode::IN_VIEW,
);
ui_graph
}

View file

@ -26,8 +26,6 @@ pub struct UiPassNode {
}
impl UiPassNode {
pub const IN_VIEW: &'static str = "view";
pub fn new(world: &mut World) -> Self {
Self {
ui_view_query: world.query_filtered(),
@ -37,10 +35,6 @@ impl UiPassNode {
}
impl Node for UiPassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![SlotInfo::new(UiPassNode::IN_VIEW, SlotType::Entity)]
}
fn update(&mut self, world: &mut World) {
self.ui_view_query.update_archetypes(world);
self.default_camera_view_query.update_archetypes(world);
@ -52,7 +46,7 @@ impl Node for UiPassNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let input_view_entity = graph.view_entity();
let Ok((transparent_phase, target, camera_ui)) =
self.ui_view_query.get_manual(world, input_view_entity)

View file

@ -15,7 +15,7 @@ use bevy::{
extract_component::{
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
},
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext, SlotInfo, SlotType},
render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext},
render_resource::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
@ -88,16 +88,6 @@ impl Plugin for PostProcessPlugin {
// Register the post process node in the 3d render graph
core_3d_graph.add_node(PostProcessNode::NAME, node);
// A slot edge tells the render graph which input/output value should be passed to the node.
// In this case, the view entity, which is the entity associated with the
// camera on which the graph is running.
core_3d_graph.add_slot_edge(
core_3d_graph.input_node().id,
core_3d::graph::input::VIEW_ENTITY,
PostProcessNode::NAME,
PostProcessNode::IN_VIEW,
);
// 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.
//
@ -118,7 +108,6 @@ struct PostProcessNode {
}
impl PostProcessNode {
pub const IN_VIEW: &str = "view";
pub const NAME: &str = "post_process";
fn new(world: &mut World) -> Self {
@ -129,14 +118,6 @@ impl PostProcessNode {
}
impl Node for PostProcessNode {
// This defines the input slot of the node and tells the render graph what
// we will need when running the node.
fn input(&self) -> Vec<SlotInfo> {
// In this case we tell the graph that our node will use the view entity.
// Currently, every node in bevy uses this pattern, so it's safe to just copy it.
vec![SlotInfo::new(PostProcessNode::IN_VIEW, SlotType::Entity)]
}
// This will run every frame before the run() method
// The important difference is that `self` is `mut` here
fn update(&mut self, world: &mut World) {
@ -158,7 +139,7 @@ impl Node for PostProcessNode {
world: &World,
) -> Result<(), NodeRunError> {
// Get the entity of the view for the render graph where this node is running
let view_entity = graph_context.get_input_entity(PostProcessNode::IN_VIEW)?;
let view_entity = graph_context.view_entity();
// We get the data we need from the world based on the view entity passed to the node.
// The data is the query that was defined earlier in the [`PostProcessNode`]