diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index 931df7943cb80..7157bc449e129 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -10,7 +10,7 @@ use crate::{ }; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, HandleUntyped}; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_math::UVec2; use bevy_reflect::TypeUuid; use bevy_render::{ @@ -19,7 +19,7 @@ use bevy_render::{ ComponentUniforms, DynamicUniformIndex, ExtractComponentPlugin, UniformComponentPlugin, }, prelude::Color, - render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner}, render_resource::*, renderer::{RenderContext, RenderDevice}, texture::{CachedTexture, TextureCache}, @@ -73,7 +73,10 @@ impl Plugin for BloomPlugin { ), ) // Add bloom to the 3d render graph - .add_render_graph_node::(CORE_3D, core_3d::graph::node::BLOOM) + .add_render_graph_node::>( + CORE_3D, + core_3d::graph::node::BLOOM, + ) .add_render_graph_edges( CORE_3D, &[ @@ -83,7 +86,10 @@ impl Plugin for BloomPlugin { ], ) // Add bloom to the 2d render graph - .add_render_graph_node::(CORE_2D, core_2d::graph::node::BLOOM) + .add_render_graph_node::>( + CORE_2D, + core_2d::graph::node::BLOOM, + ) .add_render_graph_edges( CORE_2D, &[ @@ -106,8 +112,10 @@ impl Plugin for BloomPlugin { } } -pub struct BloomNode { - view_query: QueryState<( +#[derive(Default)] +struct BloomNode; +impl ViewNode for BloomNode { + type ViewQuery = ( &'static ExtractedCamera, &'static ViewTarget, &'static BloomTexture, @@ -116,36 +124,16 @@ pub struct BloomNode { &'static BloomSettings, &'static UpsamplingPipelineIds, &'static BloomDownsamplingPipelineIds, - )>, -} - -impl FromWorld for BloomNode { - fn from_world(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, @@ -154,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(()) }; + ): QueryItem, + 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 d375094c665dd..19e0375f670ab 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -27,7 +27,7 @@ use bevy_ecs::prelude::*; use bevy_render::{ camera::Camera, extract_component::ExtractComponentPlugin, - render_graph::{EmptyNode, RenderGraphApp}, + render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner}, render_phase::{ batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, @@ -65,24 +65,22 @@ impl Plugin for Core2dPlugin { ), ); - { - use graph::node::*; - render_app - .add_render_sub_graph(CORE_2D) - .add_render_graph_node::(CORE_2D, MAIN_PASS) - .add_render_graph_node::(CORE_2D, TONEMAPPING) - .add_render_graph_node::(CORE_2D, END_MAIN_PASS_POST_PROCESSING) - .add_render_graph_node::(CORE_2D, UPSCALING) - .add_render_graph_edges( - CORE_2D, - &[ - MAIN_PASS, - TONEMAPPING, - END_MAIN_PASS_POST_PROCESSING, - UPSCALING, - ], - ); - } + use graph::node::*; + render_app + .add_render_sub_graph(CORE_2D) + .add_render_graph_node::(CORE_2D, MAIN_PASS) + .add_render_graph_node::>(CORE_2D, TONEMAPPING) + .add_render_graph_node::(CORE_2D, END_MAIN_PASS_POST_PROCESSING) + .add_render_graph_node::>(CORE_2D, UPSCALING) + .add_render_graph_edges( + CORE_2D, + &[ + MAIN_PASS, + TONEMAPPING, + END_MAIN_PASS_POST_PROCESSING, + UPSCALING, + ], + ); } } 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 d8a97deb7fa4b..34d8c299c94c9 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 @@ -4,64 +4,46 @@ use crate::{ prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass}, skybox::{SkyboxBindGroup, SkyboxPipelineId}, }; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::ExtractedCamera, - render_graph::{Node, NodeRunError, RenderGraphContext}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, render_phase::RenderPhase, render_resource::{ LoadOp, Operations, PipelineCache, RenderPassDepthStencilAttachment, RenderPassDescriptor, }, renderer::RenderContext, - view::{ExtractedView, ViewDepthTexture, ViewTarget, ViewUniformOffset}, + view::{ViewDepthTexture, ViewTarget, ViewUniformOffset}, }; #[cfg(feature = "trace")] 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>, - Option<&'static SkyboxPipelineId>, - Option<&'static SkyboxBindGroup>, - &'static ViewUniformOffset, - ), - With, - >, -} - -impl FromWorld for MainOpaquePass3dNode { - fn from_world(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); - } +/// A [`bevy_render::render_graph::Node`] that runs the [`Opaque3d`] and [`AlphaMask3d`] [`RenderPhase`]. +#[derive(Default)] +pub struct MainOpaquePass3dNode; +impl ViewNode for MainOpaquePass3dNode { + type ViewQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static RenderPhase, + &'static Camera3d, + &'static ViewTarget, + &'static ViewDepthTexture, + Option<&'static DepthPrepass>, + Option<&'static NormalPrepass>, + Option<&'static MotionVectorPrepass>, + Option<&'static SkyboxPipelineId>, + Option<&'static SkyboxBindGroup>, + &'static ViewUniformOffset, + ); 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, @@ -74,11 +56,9 @@ impl Node for MainOpaquePass3dNode { skybox_pipeline, skybox_bind_group, view_uniform_offset, - )) = self.query.get_manual(world, view_entity) else { - // No window - return Ok(()); - }; - + ): QueryItem, + 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")] @@ -125,6 +105,8 @@ impl Node for MainOpaquePass3dNode { render_pass.set_camera_viewport(viewport); } + let view_entity = graph.view_entity(); + // Opaque draws opaque_phase.render(&mut render_pass, world, view_entity); 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 e3937e59a280e..ce5a2127f1d44 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,35 @@ use crate::core_3d::Transparent3d; -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, query::QueryItem}; 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 FromWorld for MainTransparentPass3dNode { - fn from_world(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); - } +/// A [`bevy_render::render_graph::Node`] that runs the [`Transparent3d`] [`RenderPhase`]. +#[derive(Default)] +pub struct MainTransparentPass3dNode; +impl ViewNode for MainTransparentPass3dNode { + type ViewQuery = ( + &'static ExtractedCamera, + &'static RenderPhase, + &'static ViewTarget, + &'static ViewDepthTexture, + ); fn run( &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (camera, transparent_phase, target, depth): QueryItem, 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 91b7f529e19b2..ef8b16c5a6791 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -36,7 +36,7 @@ use bevy_render::{ camera::{Camera, ExtractedCamera}, extract_component::ExtractComponentPlugin, prelude::Msaa, - render_graph::{EmptyNode, RenderGraphApp}, + render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner}, render_phase::{ sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase, @@ -93,14 +93,20 @@ impl Plugin for Core3dPlugin { use graph::node::*; render_app .add_render_sub_graph(CORE_3D) - .add_render_graph_node::(CORE_3D, PREPASS) + .add_render_graph_node::>(CORE_3D, PREPASS) .add_render_graph_node::(CORE_3D, START_MAIN_PASS) - .add_render_graph_node::(CORE_3D, MAIN_OPAQUE_PASS) - .add_render_graph_node::(CORE_3D, MAIN_TRANSPARENT_PASS) + .add_render_graph_node::>( + CORE_3D, + MAIN_OPAQUE_PASS, + ) + .add_render_graph_node::>( + CORE_3D, + MAIN_TRANSPARENT_PASS, + ) .add_render_graph_node::(CORE_3D, END_MAIN_PASS) - .add_render_graph_node::(CORE_3D, TONEMAPPING) + .add_render_graph_node::>(CORE_3D, TONEMAPPING) .add_render_graph_node::(CORE_3D, END_MAIN_PASS_POST_PROCESSING) - .add_render_graph_node::(CORE_3D, UPSCALING) + .add_render_graph_node::>(CORE_3D, UPSCALING) .add_render_graph_edges( CORE_3D, &[ diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index 49a56747713db..28dfd35966317 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -14,6 +14,7 @@ use bevy_render::{ extract_component::{ExtractComponent, ExtractComponentPlugin}, prelude::Camera, render_graph::RenderGraphApp, + render_graph::ViewNodeRunner, render_resource::*, renderer::RenderDevice, texture::BevyDefault, @@ -94,7 +95,7 @@ impl Plugin for FxaaPlugin { render_app .init_resource::>() .add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare)) - .add_render_graph_node::(CORE_3D, core_3d::graph::node::FXAA) + .add_render_graph_node::>(CORE_3D, core_3d::graph::node::FXAA) .add_render_graph_edges( CORE_3D, &[ @@ -103,7 +104,7 @@ impl Plugin for FxaaPlugin { core_3d::graph::node::END_MAIN_PASS_POST_PROCESSING, ], ) - .add_render_graph_node::(CORE_2D, core_2d::graph::node::FXAA) + .add_render_graph_node::>(CORE_2D, core_2d::graph::node::FXAA) .add_render_graph_edges( CORE_2D, &[ diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index 2e0aeba454418..2daaf0584d981 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -2,60 +2,41 @@ use std::sync::Mutex; use crate::fxaa::{CameraFxaaPipeline, Fxaa, FxaaPipeline}; use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::query::QueryItem; 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; +#[derive(Default)] pub struct FxaaNode { - query: QueryState< - ( - &'static ViewTarget, - &'static CameraFxaaPipeline, - &'static Fxaa, - ), - With, - >, cached_texture_bind_group: Mutex>, } -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 ViewQuery = ( + &'static ViewTarget, + &'static CameraFxaaPipeline, + &'static Fxaa, + ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (target, pipeline, fxaa): QueryItem, 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 b9eb2c8870aef..a0d299bf88ff2 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::QueryItem; +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,32 @@ 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, - >, -} +#[derive(Default)] +pub struct PrepassNode; -impl FromWorld for PrepassNode { - fn from_world(world: &mut World) -> Self { - Self { - main_view_query: QueryState::new(world), - } - } -} - -impl Node for PrepassNode { - fn update(&mut self, world: &mut World) { - self.main_view_query.update_archetypes(world); - } +impl ViewNode for PrepassNode { + type ViewQuery = ( + &'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(()); - }; + ): QueryItem, + 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 38e5cfe7f68e9..e3da4aa03e417 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -2,11 +2,10 @@ use std::sync::Mutex; use crate::tonemapping::{TonemappingLuts, TonemappingPipeline, ViewTonemappingPipeline}; -use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::{prelude::*, query::QueryItem}; 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 +13,34 @@ use bevy_render::{ }, renderer::RenderContext, texture::Image, - view::{ExtractedView, ViewTarget, ViewUniformOffset, ViewUniforms}, + view::{ViewTarget, ViewUniformOffset, ViewUniforms}, }; use super::{get_lut_bindings, Tonemapping}; +#[derive(Default)] pub struct TonemappingNode { - query: QueryState< - ( - &'static ViewUniformOffset, - &'static ViewTarget, - &'static ViewTonemappingPipeline, - &'static Tonemapping, - ), - With, - >, cached_bind_group: Mutex>, last_tonemapping: Mutex>, } -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 ViewQuery = ( + &'static ViewUniformOffset, + &'static ViewTarget, + &'static ViewTonemappingPipeline, + &'static Tonemapping, + ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (view_uniform_offset, target, view_tonemapping_pipeline, tonemapping): QueryItem< + Self::ViewQuery, + >, 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 +48,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 fe71d3eba7a40..ae20f46a028b6 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -1,61 +1,40 @@ use crate::{blit::BlitPipeline, upscaling::ViewUpscalingPipeline}; -use bevy_ecs::prelude::*; -use bevy_ecs::query::QueryState; +use bevy_ecs::{prelude::*, query::QueryItem}; 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; +#[derive(Default)] pub struct UpscalingNode { - query: QueryState< - ( - &'static ViewTarget, - &'static ViewUpscalingPipeline, - Option<&'static ExtractedCamera>, - ), - With, - >, cached_texture_bind_group: Mutex>, } -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 ViewQuery = ( + &'static ViewTarget, + &'static ViewUpscalingPipeline, + Option<&'static ExtractedCamera>, + ); fn run( &self, - graph: &mut RenderGraphContext, + _graph: &mut RenderGraphContext, render_context: &mut RenderContext, + (target, upscaling_target, camera): QueryItem, 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 8b5143a640439..9307c235a062e 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::{QueryItem, QueryState, ReadOnlyWorldQuery}, + world::{FromWorld, World}, +}; use downcast_rs::{impl_downcast, Downcast}; use std::{borrow::Cow, fmt::Debug}; use thiserror::Error; @@ -332,3 +335,75 @@ impl Node for RunGraphOnViewNode { Ok(()) } } + +/// This trait should be used instead of the [`Node`] trait when making a render node that runs on a view. +/// +/// It is intended to be used with [`ViewNodeRunner`] +pub trait ViewNode { + /// The query that will be used on the view entity. + /// It is guaranteed to run on the view entity, so there's no need for a filter + type ViewQuery: ReadOnlyWorldQuery; + + /// Updates internal node state using the current render [`World`] prior to the run method. + fn update(&mut self, _world: &mut World) {} + + /// Runs the graph node logic, issues draw calls, updates the output slots and + /// optionally queues up subgraphs for execution. The graph data, input and output values are + /// passed via the [`RenderGraphContext`]. + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + view_query: QueryItem, + world: &World, + ) -> Result<(), NodeRunError>; +} + +/// This [`Node`] can be used to run any [`ViewNode`]. +/// It will take care of updating the view query in `update()` and running the query in `run()`. +/// +/// This [`Node`] exists to help reduce boilerplate when making a render node that runs on a view. +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 { + 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()) + else { return Ok(()); }; + + ViewNode::run(&self.node, graph, render_context, view, world)?; + Ok(()) + } +}