bevy_post_process/
msaa_writeback.rs1use bevy_app::{App, Plugin};
2use bevy_camera::MsaaWriteback;
3use bevy_color::LinearRgba;
4use bevy_core_pipeline::{
5 blit::{BlitPipeline, BlitPipelineKey},
6 core_2d::graph::{Core2d, Node2d},
7 core_3d::graph::{Core3d, Node3d},
8};
9use bevy_ecs::{prelude::*, query::QueryItem};
10use bevy_render::{
11 camera::ExtractedCamera,
12 diagnostic::RecordDiagnostics,
13 render_graph::{NodeRunError, RenderGraphContext, RenderGraphExt, ViewNode, ViewNodeRunner},
14 render_resource::*,
15 renderer::RenderContext,
16 view::{Msaa, ViewTarget},
17 Render, RenderApp, RenderSystems,
18};
19
20#[derive(Default)]
23pub struct MsaaWritebackPlugin;
24
25impl Plugin for MsaaWritebackPlugin {
26 fn build(&self, app: &mut App) {
27 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
28 return;
29 };
30 render_app.add_systems(
31 Render,
32 prepare_msaa_writeback_pipelines.in_set(RenderSystems::Prepare),
33 );
34 {
35 render_app
36 .add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
37 Core2d,
38 Node2d::MsaaWriteback,
39 )
40 .add_render_graph_edge(Core2d, Node2d::MsaaWriteback, Node2d::StartMainPass);
41 }
42 {
43 render_app
44 .add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
45 Core3d,
46 Node3d::MsaaWriteback,
47 )
48 .add_render_graph_edge(Core3d, Node3d::MsaaWriteback, Node3d::StartMainPass);
49 }
50 }
51}
52
53#[derive(Default)]
54pub struct MsaaWritebackNode;
55
56impl ViewNode for MsaaWritebackNode {
57 type ViewQuery = (
58 &'static ViewTarget,
59 &'static MsaaWritebackBlitPipeline,
60 &'static Msaa,
61 );
62
63 fn run<'w>(
64 &self,
65 _graph: &mut RenderGraphContext,
66 render_context: &mut RenderContext<'w>,
67 (target, blit_pipeline_id, msaa): QueryItem<'w, '_, Self::ViewQuery>,
68 world: &'w World,
69 ) -> Result<(), NodeRunError> {
70 if *msaa == Msaa::Off {
71 return Ok(());
72 }
73
74 let blit_pipeline = world.resource::<BlitPipeline>();
75 let pipeline_cache = world.resource::<PipelineCache>();
76 let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
77 return Ok(());
78 };
79
80 let diagnostics = render_context.diagnostic_recorder();
81
82 let post_process = target.post_process_write();
86
87 let pass_descriptor = RenderPassDescriptor {
88 label: Some("msaa_writeback"),
89 color_attachments: &[Some(RenderPassColorAttachment {
93 view: target.sampled_main_texture_view().unwrap(),
95 depth_slice: None,
96 resolve_target: Some(post_process.destination),
97 ops: Operations {
98 load: LoadOp::Clear(LinearRgba::BLACK.into()),
99 store: StoreOp::Store,
100 },
101 })],
102 depth_stencil_attachment: None,
103 timestamp_writes: None,
104 occlusion_query_set: None,
105 };
106
107 let bind_group = blit_pipeline.create_bind_group(
108 render_context.render_device(),
109 post_process.source,
110 pipeline_cache,
111 );
112
113 let mut render_pass = render_context
114 .command_encoder()
115 .begin_render_pass(&pass_descriptor);
116 let pass_span = diagnostics.pass_span(&mut render_pass, "msaa_writeback");
117
118 render_pass.set_pipeline(pipeline);
119 render_pass.set_bind_group(0, &bind_group, &[]);
120 render_pass.draw(0..3, 0..1);
121
122 pass_span.end(&mut render_pass);
123
124 Ok(())
125 }
126}
127
128#[derive(Component)]
129pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);
130
131fn prepare_msaa_writeback_pipelines(
132 mut commands: Commands,
133 pipeline_cache: Res<PipelineCache>,
134 mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
135 blit_pipeline: Res<BlitPipeline>,
136 view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,
137) {
138 for (entity, view_target, camera, msaa) in view_targets.iter() {
139 let should_writeback = match camera.msaa_writeback {
141 MsaaWriteback::Off => false,
142 MsaaWriteback::Auto => camera.sorted_camera_index_for_target > 0,
143 MsaaWriteback::Always => true,
144 };
145
146 if msaa.samples() > 1 && should_writeback {
147 let key = BlitPipelineKey {
148 texture_format: view_target.main_texture_format(),
149 samples: msaa.samples(),
150 blend_state: None,
151 };
152
153 let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
154 commands
155 .entity(entity)
156 .insert(MsaaWritebackBlitPipeline(pipeline));
157 } else {
158 commands
161 .entity(entity)
162 .remove::<MsaaWritebackBlitPipeline>();
163 }
164 }
165}