From 3fb12858dd4646e3dcac6a240865ea1d85bde763 Mon Sep 17 00:00:00 2001 From: IceSentry Date: Fri, 31 Mar 2023 15:23:00 -0400 Subject: [PATCH] wip wip --- crates/bevy_core_pipeline/src/bloom/mod.rs | 48 ++++-------- crates/bevy_core_pipeline/src/core_2d/mod.rs | 6 +- .../src/core_3d/main_opaque_pass_3d_node.rs | 68 ++++++---------- .../core_3d/main_transparent_pass_3d_node.rs | 51 ++++-------- crates/bevy_core_pipeline/src/core_3d/mod.rs | 15 ++-- crates/bevy_core_pipeline/src/fxaa/mod.rs | 6 +- crates/bevy_core_pipeline/src/fxaa/node.rs | 35 +++------ crates/bevy_core_pipeline/src/prepass/node.rs | 53 +++++-------- .../src/tonemapping/node.rs | 45 ++++------- .../bevy_core_pipeline/src/upscaling/node.rs | 40 ++++------ crates/bevy_render/src/render_graph/node.rs | 77 ++++++++++++++++++- examples/shader/post_process_pass.rs | 42 +++------- 12 files changed, 217 insertions(+), 269 deletions(-) diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index e408480b52e536..98a602e407bbf9 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::::from_world(&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::::from_world(&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,10 @@ impl Plugin for BloomPlugin { } } -pub struct BloomNode { - view_query: QueryState<( +#[derive(Default)] +struct BloomNode; +impl ViewNode for BloomNode { + type ViewWorldQuery = ( &'static ExtractedCamera, &'static ViewTarget, &'static BloomTexture, @@ -123,36 +125,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 +143,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..eed608a940a573 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,59 +3,41 @@ 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; 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); - } +/// A [`bevy_render::render_graph::Node`] that runs the [`Opaque3d`] and [`AlphaMask3d`] [`RenderPhase`]. +#[derive(Default)] +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 +46,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 +95,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..6c43a773da1046 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::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); - } +/// A [`bevy_render::render_graph::Node`] that runs the [`Transparent3d`] [`RenderPhase`]. +#[derive(Default)] +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..6c70dd422dc9d1 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,14 @@ 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::::from_world(&mut render_app.world); + let transparent_node_3d = + ViewNodeRunner::::from_world(&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..b5b31a3eff1aa1 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 Default for FxaaNode { + fn default() -> 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..c4b57782cfa646 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,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 ViewWorldQuery: 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: ROQueryItem, + 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(()) + } +} 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::();