Path: blob/main/crates/bevy_post_process/src/msaa_writeback.rs
9358 views
use bevy_app::{App, Plugin};1use bevy_camera::MsaaWriteback;2use bevy_color::LinearRgba;3use bevy_core_pipeline::{4blit::{BlitPipeline, BlitPipelineKey},5schedule::{Core2d, Core2dSystems, Core3d, Core3dSystems},6};7use bevy_ecs::prelude::*;8use bevy_render::{9camera::ExtractedCamera,10diagnostic::RecordDiagnostics,11render_resource::*,12renderer::{RenderContext, ViewQuery},13view::{Msaa, ViewTarget},14Render, RenderApp, RenderSystems,15};1617/// This enables "msaa writeback" support for the `core_2d` and `core_3d` pipelines, which can be enabled on cameras18/// using [`bevy_camera::Camera::msaa_writeback`]. See the docs on that field for more information.19#[derive(Default)]20pub struct MsaaWritebackPlugin;2122impl Plugin for MsaaWritebackPlugin {23fn build(&self, app: &mut App) {24let Some(render_app) = app.get_sub_app_mut(RenderApp) else {25return;26};27render_app.add_systems(28Render,29prepare_msaa_writeback_pipelines.in_set(RenderSystems::Prepare),30);31render_app.add_systems(Core3d, msaa_writeback.before(Core3dSystems::MainPass));32render_app.add_systems(Core2d, msaa_writeback.before(Core2dSystems::MainPass));33}34}3536pub(crate) fn msaa_writeback(37view: ViewQuery<(&ViewTarget, &MsaaWritebackBlitPipeline, &Msaa)>,38blit_pipeline: Res<BlitPipeline>,39pipeline_cache: Res<PipelineCache>,40mut ctx: RenderContext,41) {42let (target, blit_pipeline_id, msaa) = view.into_inner();4344if *msaa == Msaa::Off {45return;46}4748let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {49return;50};5152// The current "main texture" needs to be bound as an input resource, and we need the "other"53// unused target to be the "resolve target" for the MSAA write. Therefore this is the same54// as a post process write!55let post_process = target.post_process_write();5657let pass_descriptor = RenderPassDescriptor {58label: Some("msaa_writeback"),59// The target's "resolve target" is the "destination" in post_process.60// We will indirectly write the results to the "destination" using61// the MSAA resolve step.62color_attachments: &[Some(RenderPassColorAttachment {63// If MSAA is enabled, then the sampled texture will always exist64view: target.sampled_main_texture_view().unwrap(),65depth_slice: None,66resolve_target: Some(post_process.destination),67ops: Operations {68load: LoadOp::Clear(LinearRgba::BLACK.into()),69store: StoreOp::Store,70},71})],72depth_stencil_attachment: None,73timestamp_writes: None,74occlusion_query_set: None,75multiview_mask: None,76};7778let bind_group =79blit_pipeline.create_bind_group(ctx.render_device(), post_process.source, &pipeline_cache);8081let diagnostics = ctx.diagnostic_recorder();82let diagnostics = diagnostics.as_deref();83let time_span = diagnostics.time_span(ctx.command_encoder(), "msaa_writeback");8485{86let mut render_pass = ctx.command_encoder().begin_render_pass(&pass_descriptor);8788render_pass.set_pipeline(pipeline);89render_pass.set_bind_group(0, &bind_group, &[]);90render_pass.draw(0..3, 0..1);91}9293time_span.end(ctx.command_encoder());94}9596#[derive(Component)]97pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);9899fn prepare_msaa_writeback_pipelines(100mut commands: Commands,101pipeline_cache: Res<PipelineCache>,102mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,103blit_pipeline: Res<BlitPipeline>,104view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,105) {106for (entity, view_target, camera, msaa) in view_targets.iter() {107// Determine if we should do MSAA writeback based on the camera's setting108let should_writeback = match camera.msaa_writeback {109MsaaWriteback::Off => false,110MsaaWriteback::Auto => camera.sorted_camera_index_for_target > 0,111MsaaWriteback::Always => true,112};113114if msaa.samples() > 1 && should_writeback {115let key = BlitPipelineKey {116texture_format: view_target.main_texture_format(),117samples: msaa.samples(),118blend_state: None,119};120121let pipeline = pipelines.specialize(&pipeline_cache, &blit_pipeline, key);122commands123.entity(entity)124.insert(MsaaWritebackBlitPipeline(pipeline));125} else {126// This isn't strictly necessary now, but if we move to retained render entity state I don't127// want this to silently break128commands129.entity(entity)130.remove::<MsaaWritebackBlitPipeline>();131}132}133}134135136