diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index 759cc2467769d..1bef6b5e95938 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -16,7 +16,7 @@ use bevy::{ extract_component::{ ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, }, - render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext}, render_resource::{ BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId, @@ -27,11 +27,15 @@ use bevy::{ }, renderer::{RenderContext, RenderDevice}, texture::BevyDefault, - view::{ExtractedView, ViewTarget}, + view::ViewTarget, RenderApp, }, utils::Duration, }; +use bevy_internal::{ + ecs::query::QueryItem, + render::render_graph::{ViewNode, ViewNodeRunner}, +}; fn main() { App::new() @@ -83,7 +87,10 @@ impl Plugin for PostProcessPlugin { // 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::( + // + // The [`ViewNodeRunner`] is a special [`Node`] that will automatically run the node for each view + // matching the [`ViewQuery`] + .add_render_graph_node::>( // Specify 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 @@ -113,60 +120,42 @@ impl Plugin for PostProcessPlugin { } } -/// The post process node used for the render graph -struct PostProcessNode { - // The node needs a query to gather data from the ECS in order to do its rendering, - // but it's not a normal system so we need to define it manually. - query: QueryState<&'static ViewTarget, With>, -} - +// The post process node used for the render graph +#[derive(Default)] +struct PostProcessNode; impl PostProcessNode { pub const NAME: &str = "post_process"; } -impl FromWorld for PostProcessNode { - fn from_world(world: &mut World) -> Self { - Self { - query: QueryState::new(world), - } - } -} - -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); - } +// The ViewNode trait is required by the ViewNodeRunner +impl ViewNode for PostProcessNode { + // The node needs a query to gather data from the ECS in order to do its rendering, + // 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; // Runs the node logic // 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, - // you'll need to make sure you have a marker component to identify which camera(s) should run the effect. + // This will run on every view on which the graph is running. + // 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( &self, - graph_context: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + view_target: QueryItem, world: &World, ) -> Result<(), NodeRunError> { - // Get the entity of the view for the render graph where this node is running - 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`] - 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 + // Get the pipeline resource that contains the global data we need + // to create the render pipeline let post_process_pipeline = world.resource::(); // 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::(); // Get the pipeline from the cache @@ -191,9 +180,11 @@ impl Node for PostProcessNode { // 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 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 .render_device() .create_bind_group(&BindGroupDescriptor { @@ -401,7 +392,8 @@ fn update_settings(mut settings: Query<&mut PostProcessSettings>, time: Res