Add ViewNode to simplify render node management (#8118)
# Objective - When writing render nodes that need a view, you always need to define a `Query` on the associated view and make sure to update it manually and query it manually. This is verbose and error prone. ## Solution - Introduce a new `ViewNode` trait and `ViewNodeRunner` `Node` that will take care of managing the associated view query automatically. - The trait is currently a passthrough of the `Node` trait. So it still has the update/run with all the same data passed in. - The `ViewNodeRunner` is the actual node that is added to the render graph and it contains the custom node. This is necessary because it's the one that takes care of updating the node. --- ## Changelog - Add `ViewNode` - Add `ViewNodeRunner` ## Notes Currently, this only handles the view query, but it could probably have a ReadOnlySystemState that would also simplify querying all the readonly resources that most render nodes currently query manually. The issue is that I don't know how to do that without a `&mut self`. At first, I tried making this a default feature of all `Node`, but I kept hitting errors related to traits and generics and stuff I'm not super comfortable with. This implementations is much simpler and keeps the default Node behaviour so isn't a breaking change ## Reviewer Notes The PR looks quite big, but the core of the PR is the changes in `render_graph/node.rs`. Every other change is simply updating existing nodes to use this new feature. ## Open questions ~~- Naming is not final, I'm opened to anything. I named it ViewQueryNode because it's a node with a managed Query on a View.~~ ~~- What to do when the query fails? All nodes using this pattern currently just `return Ok(())` when it fails, so I chose that, but should it be more flexible?~~ ~~- Is the ViewQueryFilter actually necessary? All view queries run on the entity that is already guaranteed to be a view. Filtering won't do much, but maybe someone wants to control an effect with the presence of a component instead of a flag.~~ ~~- What to do with Nodes that are empty struct? Implementing `FromWorld` is pretty verbose but not implementing it means there's 2 ways to create a `ViewNodeRunner` which seems less ideal. This is an issue now because most node simply existed to hold the query, but now that they don't hold the query state we are left with a bunch of empty structs.~~ - Should we have a `RenderGraphApp::add_render_graph_view_node()`, this isn't necessary, but it could make the code a bit shorter. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
fe57b9f744
commit
613b5a69ae
@ -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::<BloomNode>(CORE_3D, core_3d::graph::node::BLOOM)
|
||||
.add_render_graph_node::<ViewNodeRunner<BloomNode>>(
|
||||
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::<BloomNode>(CORE_2D, core_2d::graph::node::BLOOM)
|
||||
.add_render_graph_node::<ViewNodeRunner<BloomNode>>(
|
||||
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::<BloomDownsamplingPipeline>();
|
||||
let pipeline_cache = world.resource::<PipelineCache>();
|
||||
let uniforms = world.resource::<ComponentUniforms<BloomUniforms>>();
|
||||
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<Self::ViewQuery>,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let downsampling_pipeline_res = world.resource::<BloomDownsamplingPipeline>();
|
||||
let pipeline_cache = world.resource::<PipelineCache>();
|
||||
let uniforms = world.resource::<ComponentUniforms<BloomUniforms>>();
|
||||
|
||||
let (
|
||||
Some(uniforms),
|
||||
|
||||
@ -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::<MainPass2dNode>(CORE_2D, MAIN_PASS)
|
||||
.add_render_graph_node::<TonemappingNode>(CORE_2D, TONEMAPPING)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_2D, END_MAIN_PASS_POST_PROCESSING)
|
||||
.add_render_graph_node::<UpscalingNode>(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::<MainPass2dNode>(CORE_2D, MAIN_PASS)
|
||||
.add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(CORE_2D, TONEMAPPING)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_2D, END_MAIN_PASS_POST_PROCESSING)
|
||||
.add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(CORE_2D, UPSCALING)
|
||||
.add_render_graph_edges(
|
||||
CORE_2D,
|
||||
&[
|
||||
MAIN_PASS,
|
||||
TONEMAPPING,
|
||||
END_MAIN_PASS_POST_PROCESSING,
|
||||
UPSCALING,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Opaque3d>,
|
||||
&'static RenderPhase<AlphaMask3d>,
|
||||
&'static Camera3d,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
Option<&'static DepthPrepass>,
|
||||
Option<&'static NormalPrepass>,
|
||||
Option<&'static MotionVectorPrepass>,
|
||||
Option<&'static SkyboxPipelineId>,
|
||||
Option<&'static SkyboxBindGroup>,
|
||||
&'static ViewUniformOffset,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
|
||||
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<Opaque3d>,
|
||||
&'static RenderPhase<AlphaMask3d>,
|
||||
&'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<Self::ViewQuery>,
|
||||
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);
|
||||
|
||||
|
||||
@ -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<Transparent3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
|
||||
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<Transparent3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
);
|
||||
fn run(
|
||||
&self,
|
||||
graph: &mut RenderGraphContext,
|
||||
render_context: &mut RenderContext,
|
||||
(camera, transparent_phase, target, depth): QueryItem<Self::ViewQuery>,
|
||||
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
|
||||
|
||||
@ -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::<PrepassNode>(CORE_3D, PREPASS)
|
||||
.add_render_graph_node::<ViewNodeRunner<PrepassNode>>(CORE_3D, PREPASS)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_3D, START_MAIN_PASS)
|
||||
.add_render_graph_node::<MainOpaquePass3dNode>(CORE_3D, MAIN_OPAQUE_PASS)
|
||||
.add_render_graph_node::<MainTransparentPass3dNode>(CORE_3D, MAIN_TRANSPARENT_PASS)
|
||||
.add_render_graph_node::<ViewNodeRunner<MainOpaquePass3dNode>>(
|
||||
CORE_3D,
|
||||
MAIN_OPAQUE_PASS,
|
||||
)
|
||||
.add_render_graph_node::<ViewNodeRunner<MainTransparentPass3dNode>>(
|
||||
CORE_3D,
|
||||
MAIN_TRANSPARENT_PASS,
|
||||
)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_3D, END_MAIN_PASS)
|
||||
.add_render_graph_node::<TonemappingNode>(CORE_3D, TONEMAPPING)
|
||||
.add_render_graph_node::<ViewNodeRunner<TonemappingNode>>(CORE_3D, TONEMAPPING)
|
||||
.add_render_graph_node::<EmptyNode>(CORE_3D, END_MAIN_PASS_POST_PROCESSING)
|
||||
.add_render_graph_node::<UpscalingNode>(CORE_3D, UPSCALING)
|
||||
.add_render_graph_node::<ViewNodeRunner<UpscalingNode>>(CORE_3D, UPSCALING)
|
||||
.add_render_graph_edges(
|
||||
CORE_3D,
|
||||
&[
|
||||
|
||||
@ -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::<SpecializedRenderPipelines<FxaaPipeline>>()
|
||||
.add_systems(Render, prepare_fxaa_pipelines.in_set(RenderSet::Prepare))
|
||||
.add_render_graph_node::<FxaaNode>(CORE_3D, core_3d::graph::node::FXAA)
|
||||
.add_render_graph_node::<ViewNodeRunner<FxaaNode>>(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::<FxaaNode>(CORE_2D, core_2d::graph::node::FXAA)
|
||||
.add_render_graph_node::<ViewNodeRunner<FxaaNode>>(CORE_2D, core_2d::graph::node::FXAA)
|
||||
.add_render_graph_edges(
|
||||
CORE_2D,
|
||||
&[
|
||||
|
||||
@ -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<ExtractedView>,
|
||||
>,
|
||||
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
||||
}
|
||||
|
||||
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<Self::ViewQuery>,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.view_entity();
|
||||
let pipeline_cache = world.resource::<PipelineCache>();
|
||||
let fxaa_pipeline = world.resource::<FxaaPipeline>();
|
||||
|
||||
let (target, pipeline, fxaa) = match self.query.get_manual(world, view_entity) {
|
||||
Ok(result) => result,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
if !fxaa.enabled {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@ -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<Opaque3dPrepass>,
|
||||
&'static RenderPhase<AlphaMask3dPrepass>,
|
||||
&'static ViewDepthTexture,
|
||||
&'static ViewPrepassTextures,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
#[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<Opaque3dPrepass>,
|
||||
&'static RenderPhase<AlphaMask3dPrepass>,
|
||||
&'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<Self::ViewQuery>,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.view_entity();
|
||||
|
||||
let mut color_attachments = vec![];
|
||||
color_attachments.push(
|
||||
|
||||
@ -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<ExtractedView>,
|
||||
>,
|
||||
cached_bind_group: Mutex<Option<(BufferId, TextureViewId, BindGroup)>>,
|
||||
last_tonemapping: Mutex<Option<Tonemapping>>,
|
||||
}
|
||||
|
||||
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::<PipelineCache>();
|
||||
let tonemapping_pipeline = world.resource::<TonemappingPipeline>();
|
||||
let gpu_images = world.get_resource::<RenderAssets<Image>>().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(());
|
||||
}
|
||||
|
||||
@ -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<ExtractedView>,
|
||||
>,
|
||||
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
|
||||
}
|
||||
|
||||
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<Self::ViewQuery>,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let view_entity = graph.view_entity();
|
||||
|
||||
let pipeline_cache = world.get_resource::<PipelineCache>().unwrap();
|
||||
let blit_pipeline = world.get_resource::<BlitPipeline>().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 {
|
||||
|
||||
@ -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<Self::ViewQuery>,
|
||||
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<N: ViewNode> {
|
||||
view_query: QueryState<N::ViewQuery>,
|
||||
node: N,
|
||||
}
|
||||
|
||||
impl<N: ViewNode> ViewNodeRunner<N> {
|
||||
pub fn new(node: N, world: &mut World) -> Self {
|
||||
Self {
|
||||
view_query: world.query_filtered(),
|
||||
node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: ViewNode + FromWorld> FromWorld for ViewNodeRunner<N> {
|
||||
fn from_world(world: &mut World) -> Self {
|
||||
Self::new(N::from_world(world), world)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Node for ViewNodeRunner<T>
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user