diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index e408480b52e536..061bf0c9a7b0bc 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -7,7 +7,7 @@ pub use settings::{BloomCompositeMode, BloomPrefilterSettings, BloomSettings}; use crate::{core_2d, core_3d}; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, HandleUntyped}; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::ROQueryItem}; use bevy_math::UVec2; use bevy_reflect::TypeUuid; use bevy_render::{ @@ -16,7 +16,7 @@ use bevy_render::{ ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin, }, prelude::Color, - render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraph, RenderGraphContext, ViewNode, ViewNodeRunner}, render_resource::*, renderer::{RenderContext, RenderDevice}, texture::{CachedTexture, TextureCache}, @@ -75,7 +75,7 @@ impl Plugin for BloomPlugin { // Add bloom to the 3d render graph { - let bloom_node = BloomNode::new(&mut render_app.world); + let bloom_node = ViewNodeRunner::new(BloomNode, &mut render_app.world); let mut graph = render_app.world.resource_mut::(); let draw_3d_graph = graph .get_sub_graph_mut(crate::core_3d::graph::NAME) @@ -94,7 +94,7 @@ impl Plugin for BloomPlugin { // Add bloom to the 2d render graph { - let bloom_node = BloomNode::new(&mut render_app.world); + let bloom_node = ViewNodeRunner::new(BloomNode, &mut render_app.world); let mut graph = render_app.world.resource_mut::(); let draw_2d_graph = graph .get_sub_graph_mut(crate::core_2d::graph::NAME) @@ -113,8 +113,9 @@ impl Plugin for BloomPlugin { } } -pub struct BloomNode { - view_query: QueryState<( +struct BloomNode; +impl ViewNode for BloomNode { + type ViewWorldQuery = ( &'static ExtractedCamera, &'static ViewTarget, &'static BloomTexture, @@ -123,36 +124,16 @@ pub struct BloomNode { &'static BloomSettings, &'static UpsamplingPipelineIds, &'static BloomDownsamplingPipelineIds, - )>, -} - -impl BloomNode { - pub fn new(world: &mut World) -> Self { - Self { - view_query: QueryState::new(world), - } - } -} - -impl Node for BloomNode { - fn update(&mut self, world: &mut World) { - self.view_query.update_archetypes(world); - } + ); // Atypically for a post-processing effect, we do not need to // use a secondary texture normally provided by view_target.post_process_write(), // instead we write into our own bloom texture and then directly back onto main. fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, - world: &World, - ) -> Result<(), NodeRunError> { - let downsampling_pipeline_res = world.resource::(); - let pipeline_cache = world.resource::(); - let uniforms = world.resource::>(); - let view_entity = graph.view_entity(); - let Ok(( + ( camera, view_target, bloom_texture, @@ -161,8 +142,12 @@ impl Node for BloomNode { bloom_settings, upsampling_pipeline_ids, downsampling_pipeline_ids, - )) = self.view_query.get_manual(world, view_entity) - else { return Ok(()) }; + ): ROQueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let downsampling_pipeline_res = world.resource::(); + let pipeline_cache = world.resource::(); + let uniforms = world.resource::>(); let ( Some(uniforms), diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index 5f866715fbb57b..12f714b5aa9b29 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -25,7 +25,7 @@ use bevy_ecs::prelude::*; use bevy_render::{ camera::Camera, extract_component::ExtractComponentPlugin, - render_graph::{EmptyNode, RenderGraph}, + render_graph::{EmptyNode, RenderGraph, ViewNodeRunner}, render_phase::{ batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, @@ -64,8 +64,8 @@ impl Plugin for Core2dPlugin { ); let pass_node_2d = MainPass2dNode::new(&mut render_app.world); - let tonemapping = TonemappingNode::new(&mut render_app.world); - let upscaling = UpscalingNode::new(&mut render_app.world); + let tonemapping = ViewNodeRunner::::from_world(&mut render_app.world); + let upscaling = ViewNodeRunner::::from_world(&mut render_app.world); let mut graph = render_app.world.resource_mut::(); let mut draw_2d_graph = RenderGraph::default(); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index b295b4bb1cc892..b0d5b68e0303c6 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -3,14 +3,14 @@ use crate::{ core_3d::{Camera3d, Opaque3d}, prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass}, }; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::ROQueryItem}; use bevy_render::{ camera::ExtractedCamera, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_phase::RenderPhase, render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor}, renderer::RenderContext, - view::{ExtractedView, ViewDepthTexture, ViewTarget}, + view::{ViewDepthTexture, ViewTarget}, }; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -18,44 +18,25 @@ use bevy_utils::tracing::info_span; use super::{AlphaMask3d, Camera3dDepthLoadOp}; /// A [`Node`] that runs the [`Opaque3d`] and [`AlphaMask3d`] [`RenderPhase`]. -pub struct MainOpaquePass3dNode { - query: QueryState< - ( - &'static ExtractedCamera, - &'static RenderPhase, - &'static RenderPhase, - &'static Camera3d, - &'static ViewTarget, - &'static ViewDepthTexture, - Option<&'static DepthPrepass>, - Option<&'static NormalPrepass>, - Option<&'static MotionVectorPrepass>, - ), - With, - >, -} - -impl MainOpaquePass3dNode { - pub fn new(world: &mut World) -> Self { - Self { - query: world.query_filtered(), - } - } -} - -impl Node for MainOpaquePass3dNode { - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } +pub struct MainOpaquePass3dNode; +impl ViewNode for MainOpaquePass3dNode { + type ViewWorldQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static RenderPhase, + &'static Camera3d, + &'static ViewTarget, + &'static ViewDepthTexture, + Option<&'static DepthPrepass>, + Option<&'static NormalPrepass>, + Option<&'static MotionVectorPrepass>, + ); fn run( &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, - world: &World, - ) -> Result<(), NodeRunError> { - let view_entity = graph.view_entity(); - let Ok(( + ( camera, opaque_phase, alpha_mask_phase, @@ -64,12 +45,10 @@ impl Node for MainOpaquePass3dNode { depth, depth_prepass, normal_prepass, - motion_vector_prepass - )) = self.query.get_manual(world, view_entity) else { - // No window - return Ok(()); - }; - + motion_vector_prepass, + ): ROQueryItem, + world: &World, + ) -> Result<(), NodeRunError> { // Run the opaque pass, sorted front-to-back // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] @@ -115,6 +94,8 @@ impl Node for MainOpaquePass3dNode { render_pass.set_camera_viewport(viewport); } + let view_entity = graph.view_entity(); + opaque_phase.render(&mut render_pass, world, view_entity); if !alpha_mask_phase.items.is_empty() { diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 7c4ddc28ae1703..734e0b10a3138d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -1,58 +1,34 @@ use crate::core_3d::Transparent3d; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::ROQueryItem}; use bevy_render::{ camera::ExtractedCamera, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_phase::RenderPhase, render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor}, renderer::RenderContext, - view::{ExtractedView, ViewDepthTexture, ViewTarget}, + view::{ViewDepthTexture, ViewTarget}, }; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; /// A [`Node`] that runs the [`Transparent3d`] [`RenderPhase`]. -pub struct MainTransparentPass3dNode { - query: QueryState< - ( - &'static ExtractedCamera, - &'static RenderPhase, - &'static ViewTarget, - &'static ViewDepthTexture, - ), - With, - >, -} - -impl MainTransparentPass3dNode { - pub fn new(world: &mut World) -> Self { - Self { - query: world.query_filtered(), - } - } -} - -impl Node for MainTransparentPass3dNode { - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } +pub struct MainTransparentPass3dNode; +impl ViewNode for MainTransparentPass3dNode { + type ViewWorldQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static ViewTarget, + &'static ViewDepthTexture, + ); fn run( &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (camera, transparent_phase, target, depth): ROQueryItem, world: &World, ) -> Result<(), NodeRunError> { let view_entity = graph.view_entity(); - let Ok(( - camera, - transparent_phase, - target, - depth, - )) = self.query.get_manual(world, view_entity) else { - // No window - return Ok(()); - }; if !transparent_phase.items.is_empty() { // Run the transparent pass, sorted back-to-front diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 4f1f807477a260..2fce6af29ff530 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -34,7 +34,7 @@ use bevy_render::{ camera::{Camera, ExtractedCamera}, extract_component::ExtractComponentPlugin, prelude::Msaa, - render_graph::{EmptyNode, RenderGraph}, + render_graph::{EmptyNode, RenderGraph, ViewNodeRunner}, render_phase::{ sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, @@ -86,11 +86,13 @@ impl Plugin for Core3dPlugin { ), ); - let prepass_node = PrepassNode::new(&mut render_app.world); - let opaque_node_3d = MainOpaquePass3dNode::new(&mut render_app.world); - let transparent_node_3d = MainTransparentPass3dNode::new(&mut render_app.world); - let tonemapping = TonemappingNode::new(&mut render_app.world); - let upscaling = UpscalingNode::new(&mut render_app.world); + let prepass_node = ViewNodeRunner::::from_world(&mut render_app.world); + let opaque_node_3d = ViewNodeRunner::new(MainOpaquePass3dNode, &mut render_app.world); + let transparent_node_3d = + ViewNodeRunner::new(MainTransparentPass3dNode, &mut render_app.world); + let tonemapping = ViewNodeRunner::::from_world(&mut render_app.world); + let upscaling = ViewNodeRunner::::from_world(&mut render_app.world); + let mut graph = render_app.world.resource_mut::(); let mut draw_3d_graph = RenderGraph::default(); diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index 6c4181d1f4350e..b37ec21e5a5638 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -9,7 +9,7 @@ use bevy_reflect::{ use bevy_render::{ extract_component::{ExtractComponent, ExtractComponentPlugin}, prelude::Camera, - render_graph::RenderGraph, + render_graph::{RenderGraph, ViewNodeRunner}, render_resource::*, renderer::RenderDevice, texture::BevyDefault, @@ -93,7 +93,7 @@ impl Plugin for FxaaPlugin { .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare)); { - let fxaa_node = FxaaNode::new(&mut render_app.world); + let fxaa_node = ViewNodeRunner::::from_world(&mut render_app.world); let mut binding = render_app.world.resource_mut::(); let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap(); @@ -109,7 +109,7 @@ impl Plugin for FxaaPlugin { ); } { - let fxaa_node = FxaaNode::new(&mut render_app.world); + let fxaa_node = ViewNodeRunner::::from_world(&mut render_app.world); let mut binding = render_app.world.resource_mut::(); let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap(); diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index 71d66ca27a34f7..6f0296be2d3a2a 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -2,60 +2,45 @@ use std::sync::Mutex; use crate::fxaa::{CameraFxaaPipeline, Fxaa, FxaaPipeline}; use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::query::ROQueryItem; +use bevy_ecs::system::lifetimeless::Read; use bevy_render::{ - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor, TextureViewId, }, renderer::RenderContext, - view::{ExtractedView, ViewTarget}, + view::ViewTarget, }; use bevy_utils::default; pub struct FxaaNode { - query: QueryState< - ( - &'static ViewTarget, - &'static CameraFxaaPipeline, - &'static Fxaa, - ), - With, - >, cached_texture_bind_group: Mutex>, } -impl FxaaNode { - pub fn new(world: &mut World) -> Self { +impl FromWorld for FxaaNode { + fn from_world(_world: &mut World) -> Self { Self { - query: QueryState::new(world), cached_texture_bind_group: Mutex::new(None), } } } -impl Node for FxaaNode { - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } +impl ViewNode for FxaaNode { + type ViewWorldQuery = (Read, Read, Read); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (target, pipeline, fxaa): ROQueryItem, world: &World, ) -> Result<(), NodeRunError> { - let view_entity = graph.view_entity(); let pipeline_cache = world.resource::(); let fxaa_pipeline = world.resource::(); - let (target, pipeline, fxaa) = match self.query.get_manual(world, view_entity) { - Ok(result) => result, - Err(_) => return Ok(()), - }; - if !fxaa.enabled { return Ok(()); }; diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 83d05cb1123571..62d69d4fe6f0bf 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -1,16 +1,17 @@ use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::query::ROQueryItem; +use bevy_render::render_graph::ViewNode; use bevy_render::{ camera::ExtractedCamera, prelude::Color, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext}, render_phase::RenderPhase, render_resource::{ LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, }, renderer::RenderContext, - view::{ExtractedView, ViewDepthTexture}, + view::ViewDepthTexture, }; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -20,48 +21,36 @@ use super::{AlphaMask3dPrepass, Opaque3dPrepass, ViewPrepassTextures}; /// Render node used by the prepass. /// /// By default, inserted before the main pass in the render graph. -pub struct PrepassNode { - main_view_query: QueryState< - ( - &'static ExtractedCamera, - &'static RenderPhase, - &'static RenderPhase, - &'static ViewDepthTexture, - &'static ViewPrepassTextures, - ), - With, - >, -} - -impl PrepassNode { - pub fn new(world: &mut World) -> Self { - Self { - main_view_query: QueryState::new(world), - } +pub struct PrepassNode; +impl FromWorld for PrepassNode { + fn from_world(_world: &mut World) -> Self { + Self } } -impl Node for PrepassNode { - fn update(&mut self, world: &mut World) { - self.main_view_query.update_archetypes(world); - } +impl ViewNode for PrepassNode { + type ViewWorldQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static RenderPhase, + &'static ViewDepthTexture, + &'static ViewPrepassTextures, + ); fn run( &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, - world: &World, - ) -> Result<(), NodeRunError> { - let view_entity = graph.view_entity(); - let Ok(( + ( camera, opaque_prepass_phase, alpha_mask_prepass_phase, view_depth_texture, view_prepass_textures, - )) = self.main_view_query.get_manual(world, view_entity) else { - return Ok(()); - }; + ): ROQueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let view_entity = graph.view_entity(); let mut color_attachments = vec![]; color_attachments.push( diff --git a/crates/bevy_core_pipeline/src/tonemapping/node.rs b/crates/bevy_core_pipeline/src/tonemapping/node.rs index eed9013ba375e4..b1d7f28b5f8150 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -2,11 +2,11 @@ use std::sync::Mutex; use crate::tonemapping::{TonemappingLuts, TonemappingPipeline, ViewTonemappingPipeline}; -use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::system::lifetimeless::Read; +use bevy_ecs::{prelude::*, query::ROQueryItem}; use bevy_render::{ render_asset::RenderAssets, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, BufferId, LoadOp, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, @@ -14,47 +14,42 @@ use bevy_render::{ }, renderer::RenderContext, texture::Image, - view::{ExtractedView, ViewTarget, ViewUniformOffset, ViewUniforms}, + view::{ViewTarget, ViewUniformOffset, ViewUniforms}, }; use super::{get_lut_bindings, Tonemapping}; pub struct TonemappingNode { - query: QueryState< - ( - &'static ViewUniformOffset, - &'static ViewTarget, - &'static ViewTonemappingPipeline, - &'static Tonemapping, - ), - With, - >, cached_bind_group: Mutex>, last_tonemapping: Mutex>, } -impl TonemappingNode { - pub fn new(world: &mut World) -> Self { +impl FromWorld for TonemappingNode { + fn from_world(_world: &mut World) -> Self { Self { - query: QueryState::new(world), cached_bind_group: Mutex::new(None), last_tonemapping: Mutex::new(None), } } } -impl Node for TonemappingNode { - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } +impl ViewNode for TonemappingNode { + type ViewWorldQuery = ( + Read, + Read, + Read, + Read, + ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (view_uniform_offset, target, view_tonemapping_pipeline, tonemapping): ROQueryItem< + Self::ViewWorldQuery, + >, world: &World, ) -> Result<(), NodeRunError> { - let view_entity = graph.view_entity(); let pipeline_cache = world.resource::(); let tonemapping_pipeline = world.resource::(); let gpu_images = world.get_resource::>().unwrap(); @@ -62,12 +57,6 @@ impl Node for TonemappingNode { let view_uniforms = &view_uniforms_resource.uniforms; let view_uniforms_id = view_uniforms.buffer().unwrap().id(); - let (view_uniform_offset, target, view_tonemapping_pipeline, tonemapping) = - match self.query.get_manual(world, view_entity) { - Ok(result) => result, - Err(_) => return Ok(()), - }; - if !target.is_hdr() { return Ok(()); } diff --git a/crates/bevy_core_pipeline/src/upscaling/node.rs b/crates/bevy_core_pipeline/src/upscaling/node.rs index 286b8096203180..f041a0c53dbc45 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -1,61 +1,47 @@ use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline}; -use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::{prelude::*, query::ROQueryItem}; use bevy_render::{ camera::{CameraOutputMode, ExtractedCamera}, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_resource::{ BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, LoadOp, Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor, TextureViewId, }, renderer::RenderContext, - view::{ExtractedView, ViewTarget}, + view::ViewTarget, }; use std::sync::Mutex; pub struct UpscalingNode { - query: QueryState< - ( - &'static ViewTarget, - &'static ViewUpscalingPipeline, - Option<&'static ExtractedCamera>, - ), - With, - >, cached_texture_bind_group: Mutex>, } -impl UpscalingNode { - pub fn new(world: &mut World) -> Self { +impl FromWorld for UpscalingNode { + fn from_world(_world: &mut World) -> Self { Self { - query: QueryState::new(world), cached_texture_bind_group: Mutex::new(None), } } } -impl Node for UpscalingNode { - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } +impl ViewNode for UpscalingNode { + type ViewWorldQuery = ( + &'static ViewTarget, + &'static ViewUpscalingPipeline, + Option<&'static ExtractedCamera>, + ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (target, upscaling_target, camera): ROQueryItem, world: &World, ) -> Result<(), NodeRunError> { - let view_entity = graph.view_entity(); - let pipeline_cache = world.get_resource::().unwrap(); let blit_pipeline = world.get_resource::().unwrap(); - let (target, upscaling_target, camera) = match self.query.get_manual(world, view_entity) { - Ok(query) => query, - Err(_) => return Ok(()), - }; - let color_attachment_load_op = if let Some(camera) = camera { match camera.output_mode { CameraOutputMode::Write { diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index fea7eee2d31bf8..42fb1221ca794e 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -6,7 +6,10 @@ use crate::{ }, renderer::RenderContext, }; -use bevy_ecs::world::World; +use bevy_ecs::{ + query::{QueryState, ROQueryItem, ReadOnlyWorldQuery}, + world::{FromWorld, World}, +}; use downcast_rs::{impl_downcast, Downcast}; use std::{borrow::Cow, fmt::Debug}; use thiserror::Error; @@ -331,3 +334,66 @@ impl Node for RunGraphOnViewNode { Ok(()) } } + +pub trait ViewNode { + type ViewWorldQuery: ReadOnlyWorldQuery; + + fn update(&mut self, _world: &mut World) {} + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + view_query: ROQueryItem, + world: &World, + ) -> Result<(), NodeRunError>; +} + +pub struct ViewNodeRunner { + view_query: QueryState, + node: N, +} + +impl ViewNodeRunner { + pub fn new(node: N, world: &mut World) -> Self { + Self { + view_query: world.query_filtered(), + node, + } + } +} + +impl FromWorld for ViewNodeRunner +where + N: ViewNode + FromWorld, +{ + fn from_world(world: &mut World) -> Self { + Self::new(N::from_world(world), world) + } +} + +impl Node for ViewNodeRunner +where + T: ViewNode + Send + Sync + 'static, +{ + fn update(&mut self, world: &mut World) { + self.view_query.update_archetypes(world); + self.node.update(world); + } + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + world: &World, + ) -> Result<(), NodeRunError> { + let Ok(view) = self + .view_query + .get_manual(world, graph.view_entity()) + // TODO consider letting the error propagate + else { return Ok(()); }; + + ViewNode::run(&self.node, graph, render_context, view, world)?; + Ok(()) + } +} diff --git a/examples/shader/post_process_pass.rs b/examples/shader/post_process_pass.rs index cc4e3275cbb558..66b81cc3f801cf 100644 --- a/examples/shader/post_process_pass.rs +++ b/examples/shader/post_process_pass.rs @@ -10,12 +10,13 @@ use bevy::{ clear_color::ClearColorConfig, core_3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state, }, + ecs::{query::ROQueryItem, system::lifetimeless::Read}, prelude::*, render::{ extract_component::{ ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, }, - render_graph::{Node, NodeRunError, RenderGraph, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraph, RenderGraphContext, ViewNode, ViewNodeRunner}, render_resource::{ BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId, @@ -26,7 +27,7 @@ use bevy::{ }, renderer::{RenderContext, RenderDevice}, texture::BevyDefault, - view::{ExtractedView, ViewTarget}, + view::ViewTarget, RenderApp, }, }; @@ -77,7 +78,7 @@ impl Plugin for PostProcessPlugin { // you need to extract it manually or with the plugin like above. // Create the node with the render world - let node = PostProcessNode::new(&mut render_app.world); + let node = ViewNodeRunner::new(PostProcessNode, &mut render_app.world); // Get the render graph for the entire app let mut graph = render_app.world.resource_mut::(); @@ -101,31 +102,14 @@ 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>, -} +struct PostProcessNode; impl PostProcessNode { pub const NAME: &str = "post_process"; - - fn new(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); - } +impl ViewNode for PostProcessNode { + type ViewWorldQuery = Read; // Runs the node logic // This is where you encode draw commands. @@ -134,19 +118,11 @@ impl Node for PostProcessNode { // you'll need to make sure you have a marker component 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: ROQueryItem, 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 let post_process_pipeline = world.resource::();