bevy_post_process/
msaa_writeback.rs1use bevy_app::{App, Plugin};
2use bevy_camera::{ClearColorConfig, MsaaWriteback};
3use bevy_color::LinearRgba;
4use bevy_core_pipeline::{
5 blit::{BlitPipeline, BlitPipelineKey},
6 schedule::{Core2d, Core2dSystems, Core3d, Core3dSystems},
7};
8use bevy_ecs::prelude::*;
9use bevy_platform::collections::HashMap;
10use bevy_render::{
11 camera::ExtractedCamera,
12 diagnostic::RecordDiagnostics,
13 render_resource::*,
14 renderer::{RenderContext, ViewQuery},
15 view::{Msaa, ViewTarget},
16 Render, RenderApp, RenderSystems,
17};
18
19#[derive(#[automatically_derived]impl ::core::default::Default for MsaaWritebackPlugin
{
#[inline]
fn default() -> Self { MsaaWritebackPlugin {} }
}Default)]
22pub struct MsaaWritebackPlugin;
23
24impl 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 render_app.add_systems(Core3d, msaa_writeback.before(Core3dSystems::MainPass));
34 render_app.add_systems(Core2d, msaa_writeback.before(Core2dSystems::MainPass));
35 }
36}
37
38pub(crate) fn msaa_writeback(
39 view: ViewQuery<(&ViewTarget, &MsaaWritebackBlitPipeline, &Msaa)>,
40 blit_pipeline: Res<BlitPipeline>,
41 pipeline_cache: Res<PipelineCache>,
42 mut ctx: RenderContext,
43) {
44 let (target, blit_pipeline_id, msaa) = view.into_inner();
45
46 if *msaa == Msaa::Off {
47 return;
48 }
49
50 let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
51 return;
52 };
53
54 let post_process = target.post_process_write();
58
59 let pass_descriptor = RenderPassDescriptor {
60 label: Some("msaa_writeback"),
61 color_attachments: &[Some(RenderPassColorAttachment {
65 view: target.sampled_main_texture_view().unwrap(),
67 depth_slice: None,
68 resolve_target: Some(post_process.destination),
69 ops: Operations {
70 load: LoadOp::Clear(LinearRgba::BLACK.into()),
71 store: StoreOp::Store,
72 },
73 })],
74 depth_stencil_attachment: None,
75 timestamp_writes: None,
76 occlusion_query_set: None,
77 multiview_mask: None,
78 };
79
80 let bind_group =
81 blit_pipeline.create_bind_group(ctx.render_device(), post_process.source, &pipeline_cache);
82
83 let diagnostics = ctx.diagnostic_recorder();
84 let diagnostics = diagnostics.as_deref();
85 let time_span = diagnostics.time_span(ctx.command_encoder(), "msaa_writeback");
86
87 {
88 let mut render_pass = ctx.command_encoder().begin_render_pass(&pass_descriptor);
89
90 render_pass.set_pipeline(pipeline);
91 render_pass.set_bind_group(0, &bind_group, &[]);
92 render_pass.draw(0..3, 0..1);
93 }
94
95 time_span.end(ctx.command_encoder());
96}
97
98#[derive(impl bevy_ecs::component::Component for MsaaWritebackBlitPipeline where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::Table;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}Component)]
99pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);
100
101fn prepare_msaa_writeback_pipelines(
102 mut commands: Commands,
103 pipeline_cache: Res<PipelineCache>,
104 mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
105 blit_pipeline: Res<BlitPipeline>,
106 view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,
107) {
108 let mut first_writer_indices = <HashMap<TextureId, usize>>::default();
112 for (_, view_target, camera, _) in view_targets.iter() {
113 first_writer_indices
114 .entry(view_target.main_texture().id())
115 .and_modify(|first| *first = (*first).min(camera.sorted_camera_index_for_target))
116 .or_insert(camera.sorted_camera_index_for_target);
117 }
118
119 for (entity, view_target, camera, msaa) in view_targets.iter() {
120 let should_writeback = match camera.msaa_writeback {
122 MsaaWriteback::Off => false,
123 MsaaWriteback::Auto => {
130 #[allow(non_exhaustive_omitted_patterns)] match camera.clear_color {
ClearColorConfig::None => true,
_ => false,
}matches!(camera.clear_color, ClearColorConfig::None)
131 || (camera.viewport.is_none()
132 && first_writer_indices[&view_target.main_texture().id()]
133 < camera.sorted_camera_index_for_target)
134 }
135 MsaaWriteback::Always => true,
136 };
137
138 if msaa.samples() > 1 && should_writeback {
139 let key = BlitPipelineKey {
140 target_format: view_target.main_texture_format(),
141 samples: msaa.samples(),
142 blend_state: None,
143 source_space: None,
144 };
145
146 let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);
147 commands
148 .entity(entity)
149 .insert(MsaaWritebackBlitPipeline(pipeline));
150 } else {
151 commands
154 .entity(entity)
155 .remove::<MsaaWritebackBlitPipeline>();
156 }
157 }
158}