Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_post_process/src/msaa_writeback.rs
6595 views
1
use bevy_app::{App, Plugin};
2
use bevy_color::LinearRgba;
3
use bevy_core_pipeline::{
4
blit::{BlitPipeline, BlitPipelineKey},
5
core_2d::graph::{Core2d, Node2d},
6
core_3d::graph::{Core3d, Node3d},
7
};
8
use bevy_ecs::{prelude::*, query::QueryItem};
9
use bevy_render::{
10
camera::ExtractedCamera,
11
diagnostic::RecordDiagnostics,
12
render_graph::{NodeRunError, RenderGraphContext, RenderGraphExt, ViewNode, ViewNodeRunner},
13
render_resource::*,
14
renderer::RenderContext,
15
view::{Msaa, ViewTarget},
16
Render, RenderApp, RenderSystems,
17
};
18
19
/// This enables "msaa writeback" support for the `core_2d` and `core_3d` pipelines, which can be enabled on cameras
20
/// using [`bevy_camera::Camera::msaa_writeback`]. See the docs on that field for more information.
21
#[derive(Default)]
22
pub struct MsaaWritebackPlugin;
23
24
impl Plugin for MsaaWritebackPlugin {
25
fn build(&self, app: &mut App) {
26
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
27
return;
28
};
29
render_app.add_systems(
30
Render,
31
prepare_msaa_writeback_pipelines.in_set(RenderSystems::Prepare),
32
);
33
{
34
render_app
35
.add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
36
Core2d,
37
Node2d::MsaaWriteback,
38
)
39
.add_render_graph_edge(Core2d, Node2d::MsaaWriteback, Node2d::StartMainPass);
40
}
41
{
42
render_app
43
.add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
44
Core3d,
45
Node3d::MsaaWriteback,
46
)
47
.add_render_graph_edge(Core3d, Node3d::MsaaWriteback, Node3d::StartMainPass);
48
}
49
}
50
}
51
52
#[derive(Default)]
53
pub struct MsaaWritebackNode;
54
55
impl ViewNode for MsaaWritebackNode {
56
type ViewQuery = (
57
&'static ViewTarget,
58
&'static MsaaWritebackBlitPipeline,
59
&'static Msaa,
60
);
61
62
fn run<'w>(
63
&self,
64
_graph: &mut RenderGraphContext,
65
render_context: &mut RenderContext<'w>,
66
(target, blit_pipeline_id, msaa): QueryItem<'w, '_, Self::ViewQuery>,
67
world: &'w World,
68
) -> Result<(), NodeRunError> {
69
if *msaa == Msaa::Off {
70
return Ok(());
71
}
72
73
let blit_pipeline = world.resource::<BlitPipeline>();
74
let pipeline_cache = world.resource::<PipelineCache>();
75
let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
76
return Ok(());
77
};
78
79
let diagnostics = render_context.diagnostic_recorder();
80
81
// The current "main texture" needs to be bound as an input resource, and we need the "other"
82
// unused target to be the "resolve target" for the MSAA write. Therefore this is the same
83
// as a post process write!
84
let post_process = target.post_process_write();
85
86
let pass_descriptor = RenderPassDescriptor {
87
label: Some("msaa_writeback"),
88
// The target's "resolve target" is the "destination" in post_process.
89
// We will indirectly write the results to the "destination" using
90
// the MSAA resolve step.
91
color_attachments: &[Some(RenderPassColorAttachment {
92
// If MSAA is enabled, then the sampled texture will always exist
93
view: target.sampled_main_texture_view().unwrap(),
94
depth_slice: None,
95
resolve_target: Some(post_process.destination),
96
ops: Operations {
97
load: LoadOp::Clear(LinearRgba::BLACK.into()),
98
store: StoreOp::Store,
99
},
100
})],
101
depth_stencil_attachment: None,
102
timestamp_writes: None,
103
occlusion_query_set: None,
104
};
105
106
let bind_group =
107
blit_pipeline.create_bind_group(render_context.render_device(), post_process.source);
108
109
let mut render_pass = render_context
110
.command_encoder()
111
.begin_render_pass(&pass_descriptor);
112
let pass_span = diagnostics.pass_span(&mut render_pass, "msaa_writeback");
113
114
render_pass.set_pipeline(pipeline);
115
render_pass.set_bind_group(0, &bind_group, &[]);
116
render_pass.draw(0..3, 0..1);
117
118
pass_span.end(&mut render_pass);
119
120
Ok(())
121
}
122
}
123
124
#[derive(Component)]
125
pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);
126
127
fn prepare_msaa_writeback_pipelines(
128
mut commands: Commands,
129
pipeline_cache: Res<PipelineCache>,
130
mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
131
blit_pipeline: Res<BlitPipeline>,
132
view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,
133
) {
134
for (entity, view_target, camera, msaa) in view_targets.iter() {
135
// only do writeback if writeback is enabled for the camera and this isn't the first camera in the target,
136
// as there is nothing to write back for the first camera.
137
if msaa.samples() > 1 && camera.msaa_writeback && camera.sorted_camera_index_for_target > 0
138
{
139
let key = BlitPipelineKey {
140
texture_format: view_target.main_texture_format(),
141
samples: msaa.samples(),
142
blend_state: None,
143
};
144
145
let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
146
commands
147
.entity(entity)
148
.insert(MsaaWritebackBlitPipeline(pipeline));
149
} else {
150
// This isn't strictly necessary now, but if we move to retained render entity state I don't
151
// want this to silently break
152
commands
153
.entity(entity)
154
.remove::<MsaaWritebackBlitPipeline>();
155
}
156
}
157
}
158
159