Add a separate ClearPass (#3209)

# Objective

- Rendering before MainPass should be possible, so clearing needs to happen in an earlier pass.
- Fixes #3190.

## Solution

- I added a "Clear" SubGraph, a "ClearPassNode" Node, that clears the color and depth attachments of all views and a "ClearNodeDriver" Node, that schedules the "ClearPassNode" before MainPass.
- Make sure that the 2d and 3d draw passes do not clear their attachments anymore.

### Notes

- It works in the way, that with the current pipeline examples nothing should have changed in their behaviour
- I would like to add an example that adds a pass inbetween ClearPass and MainPass, but I do not understand enough about the new render architecture to do that yet
- Clears all attachment for all views: I do not know enough about rendering in general to say, whether there is a use case for not clearing
- Does not solve #3043 as we still need Cameras/ViewTargets to clear.
This commit is contained in:
Bude 2021-12-09 21:52:32 +00:00
parent 4c91613ae6
commit 82c04f93f5
5 changed files with 114 additions and 9 deletions

View file

@ -0,0 +1,63 @@
use crate::ClearColor;
use bevy_ecs::prelude::*;
use bevy_render2::{
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo},
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
renderer::RenderContext,
view::{ExtractedView, ViewDepthTexture, ViewTarget},
};
pub struct ClearPassNode {
query: QueryState<(&'static ViewTarget, &'static ViewDepthTexture), With<ExtractedView>>,
}
impl ClearPassNode {
pub fn new(world: &mut World) -> Self {
Self {
query: QueryState::new(world),
}
}
}
impl Node for ClearPassNode {
fn input(&self) -> Vec<SlotInfo> {
vec![]
}
fn update(&mut self, world: &mut World) {
self.query.update_archetypes(world);
}
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
/* This gets all ViewTargets and ViewDepthTextures and clears its attachments */
for (target, depth) in self.query.iter_manual(world) {
let clear_color = world.get_resource::<ClearColor>().unwrap();
let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"),
color_attachments: &[target.get_color_attachment(Operations {
load: LoadOp::Clear(clear_color.0.into()),
store: true,
})],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
depth_ops: Some(Operations {
load: LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
}),
};
render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
}
Ok(())
}
}

View file

@ -0,0 +1,20 @@
use bevy_ecs::world::World;
use bevy_render2::{
render_graph::{Node, NodeRunError, RenderGraphContext},
renderer::RenderContext,
};
pub struct ClearPassDriverNode;
impl Node for ClearPassDriverNode {
fn run(
&self,
graph: &mut RenderGraphContext,
_render_context: &mut RenderContext,
_world: &World,
) -> Result<(), NodeRunError> {
graph.run_sub_graph(crate::clear_graph::NAME, vec![])?;
Ok(())
}
}

View file

@ -1,7 +1,11 @@
mod clear_pass;
mod clear_pass_driver;
mod main_pass_2d;
mod main_pass_3d;
mod main_pass_driver;
pub use clear_pass::*;
pub use clear_pass_driver::*;
pub use main_pass_2d::*;
pub use main_pass_3d::*;
pub use main_pass_driver::*;
@ -24,6 +28,8 @@ use bevy_render2::{
RenderApp, RenderStage, RenderWorld,
};
use crate::clear_pass::ClearPassNode;
/// Resource that configures the clear color
#[derive(Clone, Debug)]
pub struct ClearColor(pub Color);
@ -42,6 +48,7 @@ impl Default for ClearColor {
pub mod node {
pub const MAIN_PASS_DEPENDENCIES: &str = "main_pass_dependencies";
pub const MAIN_PASS_DRIVER: &str = "main_pass_driver";
pub const CLEAR_PASS_DRIVER: &str = "clear_pass_driver";
pub const VIEW: &str = "view";
}
@ -65,6 +72,13 @@ pub mod draw_3d_graph {
}
}
pub mod clear_graph {
pub const NAME: &str = "clear";
pub mod node {
pub const CLEAR_PASS: &str = "clear_pass";
}
}
#[derive(Default)]
pub struct CorePipelinePlugin;
@ -86,6 +100,7 @@ impl Plugin for CorePipelinePlugin {
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<AlphaMask3d>)
.add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<Transparent3d>);
let clear_pass_node = ClearPassNode::new(&mut render_app.world);
let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
let mut graph = render_app.world.get_resource_mut::<RenderGraph>().unwrap();
@ -122,11 +137,19 @@ impl Plugin for CorePipelinePlugin {
.unwrap();
graph.add_sub_graph(draw_3d_graph::NAME, draw_3d_graph);
let mut clear_graph = RenderGraph::default();
clear_graph.add_node(clear_graph::node::CLEAR_PASS, clear_pass_node);
graph.add_sub_graph(clear_graph::NAME, clear_graph);
graph.add_node(node::MAIN_PASS_DEPENDENCIES, EmptyNode);
graph.add_node(node::MAIN_PASS_DRIVER, MainPassDriverNode);
graph
.add_node_edge(node::MAIN_PASS_DEPENDENCIES, node::MAIN_PASS_DRIVER)
.unwrap();
graph.add_node(node::CLEAR_PASS_DRIVER, ClearPassDriverNode);
graph
.add_node_edge(node::CLEAR_PASS_DRIVER, node::MAIN_PASS_DRIVER)
.unwrap();
}
}

View file

@ -1,4 +1,4 @@
use crate::{ClearColor, Transparent2d};
use crate::Transparent2d;
use bevy_ecs::prelude::*;
use bevy_render2::{
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
@ -43,14 +43,14 @@ impl Node for MainPass2dNode {
.query
.get_manual(world, view_entity)
.expect("view entity should exist");
let clear_color = world.get_resource::<ClearColor>().unwrap();
let pass_descriptor = RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[RenderPassColorAttachment {
view: &target.view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(clear_color.0.into()),
load: LoadOp::Load,
store: true,
},
}],

View file

@ -1,4 +1,4 @@
use crate::{AlphaMask3d, ClearColor, Opaque3d, Transparent3d};
use crate::{AlphaMask3d, Opaque3d, Transparent3d};
use bevy_ecs::prelude::*;
use bevy_render2::{
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
@ -51,24 +51,23 @@ impl Node for MainPass3dNode {
.query
.get_manual(world, view_entity)
.expect("view entity should exist");
let clear_color = world.get_resource::<ClearColor>().unwrap();
{
// Run the opaque pass, sorted front-to-back
// NOTE: Scoped to drop the mutable borrow of render_context
let pass_descriptor = RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
// NOTE: The opaque pass clears and initializes the color
// NOTE: The opaque pass loads the color
// buffer as well as writing to it.
color_attachments: &[target.get_color_attachment(Operations {
load: LoadOp::Clear(clear_color.0.into()),
load: LoadOp::Load,
store: true,
})],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: The opaque main pass clears and writes to the depth buffer.
// NOTE: The opaque main pass loads the depth buffer and possibly overwrites it
depth_ops: Some(Operations {
load: LoadOp::Clear(0.0),
load: LoadOp::Load,
store: true,
}),
stencil_ops: None,