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