Skip to content

Commit fe22423

Browse files
committed
customizable edges
1 parent f51a835 commit fe22423

File tree

3 files changed

+82
-35
lines changed

3 files changed

+82
-35
lines changed

crates/bevy_pbr/src/fullscreen_material.rs

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ use std::marker::PhantomData;
44

55
use bevy_app::{App, Plugin};
66
use bevy_asset::AssetServer;
7-
use bevy_core_pipeline::{
8-
core_3d::graph::{Core3d, Node3d},
9-
FullscreenShader,
10-
};
7+
use bevy_core_pipeline::{core_3d::graph::Core3d, FullscreenShader};
118
use bevy_ecs::{
129
component::Component,
1310
query::QueryItem,
@@ -22,7 +19,8 @@ use bevy_render::{
2219
UniformComponentPlugin,
2320
},
2421
render_graph::{
25-
NodeRunError, RenderGraphContext, RenderGraphExt, RenderLabel, ViewNode, ViewNodeRunner,
22+
InternedRenderLabel, NodeRunError, RenderGraph, RenderGraphContext, RenderGraphError,
23+
RenderGraphExt, ViewNode, ViewNodeRunner,
2624
},
2725
render_resource::{
2826
binding_types::{sampler, texture_2d, uniform_buffer},
@@ -38,8 +36,7 @@ use bevy_render::{
3836
RenderApp, RenderStartup,
3937
};
4038
use bevy_utils::default;
41-
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
42-
struct PostProcessLabel;
39+
use tracing::warn;
4340

4441
#[derive(Default)]
4542
pub struct FullscreenMaterialPlugin<T: FullscreenMaterial> {
@@ -57,35 +54,49 @@ impl<T: FullscreenMaterial> Plugin for FullscreenMaterialPlugin<T> {
5754
};
5855
render_app.add_systems(RenderStartup, init_pipeline::<T>);
5956

60-
// TODO make this configurable so it's not hardcoded to 3d
61-
render_app
62-
.add_render_graph_node::<ViewNodeRunner<FullscreenMaterialNode<T>>>(
63-
Core3d,
64-
PostProcessLabel,
65-
)
66-
.add_render_graph_edges(
67-
Core3d,
68-
(
69-
Node3d::Tonemapping,
70-
// TODO make this configurable
71-
PostProcessLabel,
72-
Node3d::EndMainPassPostProcessing,
73-
),
74-
);
57+
// TODO make this more configurable so it's not hardcoded to 3d
58+
render_app.add_render_graph_node::<ViewNodeRunner<FullscreenMaterialNode<T>>>(
59+
Core3d,
60+
T::node_label(),
61+
);
62+
// We can't use add_render_graph_edges because it doesn't accept a Vec<RenderLabel>
63+
if let Some(mut render_graph) = render_app.world_mut().get_resource_mut::<RenderGraph>()
64+
&& let Some(graph) = render_graph.get_sub_graph_mut(Core3d)
65+
{
66+
for window in T::node_edges().windows(2) {
67+
let [a, b] = window else {
68+
break;
69+
};
70+
let Err(err) = graph.try_add_node_edge(*a, *b) else {
71+
continue;
72+
};
73+
match err {
74+
// Already existing edges are very easy to produce with this api
75+
// and shouldn't cause a panic
76+
RenderGraphError::EdgeAlreadyExists(_) => {}
77+
_ => panic!("{err:?}"),
78+
}
79+
}
80+
} else {
81+
warn!("Failed to add edges for FullscreenMaterial");
82+
};
7583
}
7684
}
7785

7886
pub trait FullscreenMaterial:
7987
Component + ExtractComponent + Clone + Copy + ShaderType + WriteInto + Default
8088
{
8189
fn fragment_shader() -> ShaderRef;
90+
fn node_label() -> InternedRenderLabel;
91+
fn node_edges() -> Vec<InternedRenderLabel>;
8292
}
8393

8494
#[derive(Resource)]
8595
struct FullscreenMaterialPipeline {
8696
layout: BindGroupLayout,
8797
sampler: Sampler,
8898
pipeline_id: CachedRenderPipelineId,
99+
pipeline_id_hdr: CachedRenderPipelineId,
89100
}
90101

91102
fn init_pipeline<T: FullscreenMaterial>(
@@ -104,7 +115,6 @@ fn init_pipeline<T: FullscreenMaterial>(
104115
texture_2d(TextureSampleType::Float { filterable: true }),
105116
// The sampler that will be used to sample the screen texture
106117
sampler(SamplerBindingType::Filtering),
107-
// The settings uniform that will control the effect
108118
uniform_buffer::<T>(true),
109119
),
110120
),
@@ -121,13 +131,12 @@ fn init_pipeline<T: FullscreenMaterial>(
121131
};
122132
// Setup a fullscreen triangle for the vertex state.
123133
let vertex_state = fullscreen_shader.to_vertex_state();
124-
let pipeline_id = pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {
134+
let mut desc = RenderPipelineDescriptor {
125135
label: Some("post_process_pipeline".into()),
126136
layout: vec![layout.clone()],
127137
vertex: vertex_state,
128138
fragment: Some(FragmentState {
129139
shader,
130-
// TODO handle HDR
131140
targets: vec![Some(ColorTargetState {
132141
format: TextureFormat::bevy_default(),
133142
blend: None,
@@ -136,11 +145,18 @@ fn init_pipeline<T: FullscreenMaterial>(
136145
..default()
137146
}),
138147
..default()
139-
});
148+
};
149+
let pipeline_id = pipeline_cache.queue_render_pipeline(desc.clone());
150+
desc.fragment.as_mut().unwrap().targets[0]
151+
.as_mut()
152+
.unwrap()
153+
.format = ViewTarget::TEXTURE_FORMAT_HDR;
154+
let pipeline_id_hdr = pipeline_cache.queue_render_pipeline(desc);
140155
commands.insert_resource(FullscreenMaterialPipeline {
141156
layout,
142157
sampler,
143158
pipeline_id,
159+
pipeline_id_hdr,
144160
});
145161
}
146162

@@ -163,12 +179,16 @@ impl<T: FullscreenMaterial> ViewNode for FullscreenMaterialNode<T> {
163179
(view_target, _post_process_settings, settings_index): QueryItem<Self::ViewQuery>,
164180
world: &World,
165181
) -> Result<(), NodeRunError> {
166-
let post_process_pipeline = world.resource::<FullscreenMaterialPipeline>();
182+
let fullscreen_pipeline = world.resource::<FullscreenMaterialPipeline>();
167183

168184
let pipeline_cache = world.resource::<PipelineCache>();
185+
let pipeline_id = if view_target.is_hdr() {
186+
fullscreen_pipeline.pipeline_id_hdr
187+
} else {
188+
fullscreen_pipeline.pipeline_id
189+
};
169190

170-
let Some(pipeline) = pipeline_cache.get_render_pipeline(post_process_pipeline.pipeline_id)
171-
else {
191+
let Some(pipeline) = pipeline_cache.get_render_pipeline(pipeline_id) else {
172192
return Ok(());
173193
};
174194

@@ -177,17 +197,16 @@ impl<T: FullscreenMaterial> ViewNode for FullscreenMaterialNode<T> {
177197
return Ok(());
178198
};
179199

200+
// We should maybe rename this because this can be used for other reasons that aren't
201+
// post-processing
180202
let post_process = view_target.post_process_write();
181203

182204
let bind_group = render_context.render_device().create_bind_group(
183205
"post_process_bind_group",
184-
&post_process_pipeline.layout,
206+
&fullscreen_pipeline.layout,
185207
&BindGroupEntries::sequential((
186-
// Make sure to use the source view
187208
post_process.source,
188-
// Use the sampler created for the pipeline
189-
&post_process_pipeline.sampler,
190-
// Set the settings binding
209+
&fullscreen_pipeline.sampler,
191210
settings_binding.clone(),
192211
)),
193212
);

crates/bevy_render/src/render_graph/node.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ pub trait IntoRenderNodeArray<const N: usize> {
3838
fn into_array(self) -> [InternedRenderLabel; N];
3939
}
4040

41+
impl<const N: usize> IntoRenderNodeArray<N> for Vec<InternedRenderLabel> {
42+
fn into_array(self) -> [InternedRenderLabel; N] {
43+
self.try_into().unwrap()
44+
}
45+
}
46+
4147
macro_rules! impl_render_label_tuples {
4248
($N: expr, $(#[$meta:meta])* $(($T: ident, $I: ident)),*) => {
4349
$(#[$meta])*

examples/shader/fullscreen_material.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
//! Demonstrates how to write a custom fullscreen shader
2+
//!
3+
//! This is currently limited to 3d only but work is in progress to make it work in 2d
24
35
use bevy::{
6+
core_pipeline::core_3d::graph::Node3d,
47
pbr::fullscreen_material::{FullscreenMaterial, FullscreenMaterialPlugin},
58
prelude::*,
69
shader::ShaderRef,
710
};
8-
use bevy_render::{extract_component::ExtractComponent, render_resource::ShaderType};
11+
use bevy_render::{
12+
extract_component::ExtractComponent,
13+
render_graph::{InternedRenderLabel, RenderLabel},
14+
render_resource::ShaderType,
15+
};
916

1017
fn main() {
1118
App::new()
@@ -42,6 +49,9 @@ fn setup(
4249
});
4350
}
4451

52+
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
53+
struct MyLabel;
54+
4555
#[derive(Component, ExtractComponent, Clone, Copy, ShaderType, Default)]
4656
struct MyPostProcessing {
4757
data: f32,
@@ -51,4 +61,16 @@ impl FullscreenMaterial for MyPostProcessing {
5161
fn fragment_shader() -> ShaderRef {
5262
"shaders/my_post_processing.wgsl".into()
5363
}
64+
65+
fn node_label() -> InternedRenderLabel {
66+
MyLabel.intern()
67+
}
68+
69+
fn node_edges() -> Vec<InternedRenderLabel> {
70+
vec![
71+
Node3d::Tonemapping.intern(),
72+
MyLabel.intern(),
73+
Node3d::EndMainPassPostProcessing.intern(),
74+
]
75+
}
5476
}

0 commit comments

Comments
 (0)