use ViewNodeRunner in the post_processing example (#9127)

- I forgot to update the example after the `ViewNodeRunner` was merged.
It was even partially mentioned in one of the comments.

- Use the `ViewNodeRunner` in the post_processing example
- I also broke up a few lines that were a bit long

---------

Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
This commit is contained in:
IceSentry 2023-08-07 19:02:32 -04:00 committed by Carter Anderson
parent 633f121502
commit 2a02929b1a

View File

@ -16,7 +16,7 @@ use bevy::{
extract_component::{ extract_component::{
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
}, },
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext},
render_resource::{ render_resource::{
BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId, BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId,
@ -27,11 +27,15 @@ use bevy::{
}, },
renderer::{RenderContext, RenderDevice}, renderer::{RenderContext, RenderDevice},
texture::BevyDefault, texture::BevyDefault,
view::{ExtractedView, ViewTarget}, view::ViewTarget,
RenderApp, RenderApp,
}, },
utils::Duration, utils::Duration,
}; };
use bevy_internal::{
ecs::query::QueryItem,
render::render_graph::{ViewNode, ViewNodeRunner},
};
fn main() { fn main() {
App::new() App::new()
@ -83,8 +87,11 @@ impl Plugin for PostProcessPlugin {
// 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`] // Add a [`Node`] to the [`RenderGraph`]
// The Node needs to impl FromWorld // 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 // The [`ViewNodeRunner`] is a special [`Node`] that will automatically run the node for each view
// matching the [`ViewQuery`]
.add_render_graph_node::<ViewNodeRunner<PostProcessNode>>(
// Specify the name of the graph, in this case we want the graph for 3d
core_3d::graph::NAME, core_3d::graph::NAME,
// It also needs the name of the node // It also needs the name of the node
PostProcessNode::NAME, PostProcessNode::NAME,
@ -113,60 +120,42 @@ impl Plugin for PostProcessPlugin {
} }
} }
/// The post process node used for the render graph // The post process node used for the render graph
struct PostProcessNode { #[derive(Default)]
// The node needs a query to gather data from the ECS in order to do its rendering, struct PostProcessNode;
// but it's not a normal system so we need to define it manually.
query: QueryState<&'static ViewTarget, With<ExtractedView>>,
}
impl PostProcessNode { impl PostProcessNode {
pub const NAME: &str = "post_process"; pub const NAME: &str = "post_process";
} }
impl FromWorld for PostProcessNode { // The ViewNode trait is required by the ViewNodeRunner
fn from_world(world: &mut World) -> Self { impl ViewNode for PostProcessNode {
Self { // The node needs a query to gather data from the ECS in order to do its rendering,
query: QueryState::new(world), // but it's not a normal system so we need to define it manually.
} //
} // This query will only run on the view entity
} type ViewQuery = &'static ViewTarget;
impl Node for PostProcessNode {
// 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) {
// Since this is not a system we need to update the query manually.
// This is mostly boilerplate. There are plans to remove this in the future.
// For now, you can just copy it.
self.query.update_archetypes(world);
}
// Runs the node logic // Runs the node logic
// This is where you encode draw commands. // This is where you encode draw commands.
// //
// This will run on every view on which the graph is running. If you don't want your effect to run on every camera, // This will run on every view on which the graph is running.
// you'll need to make sure you have a marker component to identify which camera(s) should run the effect. // If you don't want your effect to run on every camera,
// you'll need to make sure you have a marker component as part of [`ViewQuery`]
// to identify which camera(s) should run the effect.
fn run( fn run(
&self, &self,
graph_context: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext, render_context: &mut RenderContext,
view_target: QueryItem<Self::ViewQuery>,
world: &World, world: &World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
// Get the entity of the view for the render graph where this node is running // Get the pipeline resource that contains the global data we need
let view_entity = graph_context.view_entity(); // to create the render pipeline
// 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`]
let Ok(view_target) = self.query.get_manual(world, view_entity) else {
return Ok(());
};
// Get the pipeline resource that contains the global data we need to create the render pipeline
let post_process_pipeline = world.resource::<PostProcessPipeline>(); let post_process_pipeline = world.resource::<PostProcessPipeline>();
// The pipeline cache is a cache of all previously created pipelines. // The pipeline cache is a cache of all previously created pipelines.
// It is required to avoid creating a new pipeline each frame, which is expensive due to shader compilation. // It is required to avoid creating a new pipeline each frame,
// which is expensive due to shader compilation.
let pipeline_cache = world.resource::<PipelineCache>(); let pipeline_cache = world.resource::<PipelineCache>();
// Get the pipeline from the cache // Get the pipeline from the cache
@ -191,9 +180,11 @@ impl Node for PostProcessNode {
// The bind_group gets created each frame. // The bind_group gets created each frame.
// //
// Normally, you would create a bind_group in the Queue set, but this doesn't work with the post_process_write(). // Normally, you would create a bind_group in the Queue set,
// but this doesn't work with the post_process_write().
// The reason it doesn't work is because each post_process_write will alternate the source/destination. // The reason it doesn't work is because each post_process_write will alternate the source/destination.
// The only way to have the correct source/destination for the bind_group is to make sure you get it during the node execution. // The only way to have the correct source/destination for the bind_group
// is to make sure you get it during the node execution.
let bind_group = render_context let bind_group = render_context
.render_device() .render_device()
.create_bind_group(&BindGroupDescriptor { .create_bind_group(&BindGroupDescriptor {
@ -407,7 +398,8 @@ fn update_settings(mut settings: Query<&mut PostProcessSettings>, time: Res<Time
// Scale it to a more reasonable level // Scale it to a more reasonable level
intensity *= 0.015; intensity *= 0.015;
// Set the intensity. This will then be extracted to the render world and uploaded to the gpu automatically. // Set the intensity.
// This will then be extracted to the render world and uploaded to the gpu automatically by the [`UniformComponentPlugin`]
setting.intensity = intensity; setting.intensity = intensity;
} }
} }