Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Msaa to component #14273

Merged
merged 14 commits into from
Jul 22, 2024
4 changes: 4 additions & 0 deletions crates/bevy_core_pipeline/src/core_2d/camera_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::core_2d::graph::Core2d;
use crate::tonemapping::{DebandDither, Tonemapping};
use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use bevy_render::prelude::Msaa;
use bevy_render::{
camera::{
Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph,
Expand Down Expand Up @@ -35,6 +36,7 @@ pub struct Camera2dBundle {
pub tonemapping: Tonemapping,
pub deband_dither: DebandDither,
pub main_texture_usages: CameraMainTextureUsages,
pub msaa: Msaa,
}

impl Default for Camera2dBundle {
Expand All @@ -58,6 +60,7 @@ impl Default for Camera2dBundle {
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(),
msaa: Default::default(),
}
}
}
Expand Down Expand Up @@ -90,6 +93,7 @@ impl Camera2dBundle {
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(),
msaa: Default::default(),
}
}
}
3 changes: 3 additions & 0 deletions crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use bevy_ecs::prelude::*;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::view::Msaa;
use bevy_render::{
camera::{Camera, CameraMainTextureUsages, CameraRenderGraph, Exposure, Projection},
extract_component::ExtractComponent,
Expand Down Expand Up @@ -152,6 +153,7 @@ pub struct Camera3dBundle {
pub color_grading: ColorGrading,
pub exposure: Exposure,
pub main_texture_usages: CameraMainTextureUsages,
pub msaa: Msaa,
}

// NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
Expand All @@ -171,6 +173,7 @@ impl Default for Camera3dBundle {
exposure: Default::default(),
main_texture_usages: Default::default(),
deband_dither: DebandDither::Enabled,
msaa: Default::default(),
}
}
}
35 changes: 22 additions & 13 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,16 +610,21 @@ pub fn extract_camera_prepass_phase(
pub fn prepare_core_3d_depth_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
views_3d: Query<(Entity, &ExtractedCamera, Option<&DepthPrepass>, &Camera3d)>,
views_3d: Query<(
Entity,
&ExtractedCamera,
Option<&DepthPrepass>,
&Camera3d,
&Msaa,
)>,
) {
let mut render_target_usage = HashMap::default();
for (view, camera, depth_prepass, camera_3d) in &views_3d {
for (view, camera, depth_prepass, camera_3d, _msaa) in &views_3d {
if !opaque_3d_phases.contains_key(&view)
|| !alpha_mask_3d_phases.contains_key(&view)
|| !transmissive_3d_phases.contains_key(&view)
Expand All @@ -641,13 +646,13 @@ pub fn prepare_core_3d_depth_textures(
}

let mut textures = HashMap::default();
for (entity, camera, _, camera_3d) in &views_3d {
for (entity, camera, _, camera_3d, msaa) in &views_3d {
let Some(physical_target_size) = camera.physical_target_size else {
continue;
};

let cached_texture = textures
.entry(camera.target.clone())
.entry((camera.target.clone(), msaa))
.or_insert_with(|| {
// The size of the depth texture
let size = Extent3d {
Expand Down Expand Up @@ -779,11 +784,8 @@ pub fn prepare_core_3d_transmission_textures(
}

// Disable MSAA and warn if using deferred rendering
pub fn check_msaa(
mut msaa: ResMut<Msaa>,
deferred_views: Query<Entity, (With<Camera>, With<DeferredPrepass>)>,
) {
if !deferred_views.is_empty() {
pub fn check_msaa(mut deferred_views: Query<&mut Msaa, (With<Camera>, With<DeferredPrepass>)>) {
for mut msaa in deferred_views.iter_mut() {
match *msaa {
Msaa::Off => (),
_ => {
Expand All @@ -799,7 +801,6 @@ pub fn check_msaa(
pub fn prepare_prepass_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
Expand All @@ -808,6 +809,7 @@ pub fn prepare_prepass_textures(
views_3d: Query<(
Entity,
&ExtractedCamera,
&Msaa,
Has<DepthPrepass>,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
Expand All @@ -819,8 +821,15 @@ pub fn prepare_prepass_textures(
let mut deferred_textures = HashMap::default();
let mut deferred_lighting_id_textures = HashMap::default();
let mut motion_vectors_textures = HashMap::default();
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
&views_3d
for (
entity,
camera,
msaa,
depth_prepass,
normal_prepass,
motion_vector_prepass,
deferred_prepass,
) in &views_3d
{
if !opaque_3d_prepass_phases.contains_key(&entity)
&& !alpha_mask_3d_prepass_phases.contains_key(&entity)
Expand Down
9 changes: 4 additions & 5 deletions crates/bevy_core_pipeline/src/dof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,10 @@ impl FromWorld for DepthOfFieldGlobalBindGroupLayout {
/// specific to each view.
pub fn prepare_depth_of_field_view_bind_group_layouts(
mut commands: Commands,
view_targets: Query<(Entity, &DepthOfFieldSettings)>,
msaa: Res<Msaa>,
view_targets: Query<(Entity, &DepthOfFieldSettings, &Msaa)>,
render_device: Res<RenderDevice>,
) {
for (view, dof_settings) in view_targets.iter() {
for (view, dof_settings, msaa) in view_targets.iter() {
// Create the bind group layout for the passes that take one input.
let single_input = render_device.create_bind_group_layout(
Some("depth of field bind group layout (single input)"),
Expand Down Expand Up @@ -646,16 +645,16 @@ pub fn prepare_depth_of_field_pipelines(
mut commands: Commands,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<DepthOfFieldPipeline>>,
msaa: Res<Msaa>,
global_bind_group_layout: Res<DepthOfFieldGlobalBindGroupLayout>,
view_targets: Query<(
Entity,
&ExtractedView,
&DepthOfFieldSettings,
&ViewDepthOfFieldBindGroupLayouts,
&Msaa,
)>,
) {
for (entity, view, dof_settings, view_bind_group_layouts) in view_targets.iter() {
for (entity, view, dof_settings, view_bind_group_layouts, msaa) in view_targets.iter() {
let dof_pipeline = DepthOfFieldPipeline {
view_bind_group_layouts: view_bind_group_layouts.clone(),
global_bind_group_layout: global_bind_group_layout.layout.clone(),
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_core_pipeline/src/motion_blur/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ impl ViewNode for MotionBlurNode {
&'static MotionBlurPipelineId,
&'static ViewPrepassTextures,
&'static MotionBlur,
&'static Msaa,
);
fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(view_target, pipeline_id, prepass_textures, settings): QueryItem<Self::ViewQuery>,
(view_target, pipeline_id, prepass_textures, settings, msaa): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
if settings.samples == 0 || settings.shutter_angle <= 0.0 {
Expand Down Expand Up @@ -60,7 +61,6 @@ impl ViewNode for MotionBlurNode {

let post_process = view_target.post_process_write();

let msaa = world.resource::<Msaa>();
let layout = if msaa.samples() == 1 {
&motion_blur_pipeline.layout
} else {
Expand Down
5 changes: 2 additions & 3 deletions crates/bevy_core_pipeline/src/motion_blur/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,9 @@ pub(crate) fn prepare_motion_blur_pipelines(
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>,
pipeline: Res<MotionBlurPipeline>,
msaa: Res<Msaa>,
views: Query<(Entity, &ExtractedView), With<MotionBlur>>,
views: Query<(Entity, &ExtractedView, &Msaa), With<MotionBlur>>,
) {
for (entity, view) in &views {
for (entity, view, msaa) in &views {
let pipeline_id = pipelines.specialize(
&pipeline_cache,
&pipeline,
Expand Down
134 changes: 66 additions & 68 deletions crates/bevy_core_pipeline/src/msaa_writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use crate::{
use bevy_app::{App, Plugin};
use bevy_color::LinearRgba;
use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryItem;
use bevy_render::render_graph::{ViewNode, ViewNodeRunner};
use bevy_render::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext},
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext},
renderer::RenderContext,
view::{Msaa, ViewTarget},
Render, RenderSet,
Expand All @@ -30,90 +32,87 @@ impl Plugin for MsaaWritebackPlugin {
);
{
render_app
.add_render_graph_node::<MsaaWritebackNode>(Core2d, Node2d::MsaaWriteback)
.add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
Core2d,
Node2d::MsaaWriteback,
)
.add_render_graph_edge(Core2d, Node2d::MsaaWriteback, Node2d::StartMainPass);
}
{
render_app
.add_render_graph_node::<MsaaWritebackNode>(Core3d, Node3d::MsaaWriteback)
.add_render_graph_node::<ViewNodeRunner<MsaaWritebackNode>>(
Core3d,
Node3d::MsaaWriteback,
)
.add_render_graph_edge(Core3d, Node3d::MsaaWriteback, Node3d::StartMainPass);
}
}
}

pub struct MsaaWritebackNode {
cameras: QueryState<(&'static ViewTarget, &'static MsaaWritebackBlitPipeline)>,
}
#[derive(Default)]
pub struct MsaaWritebackNode;

impl FromWorld for MsaaWritebackNode {
fn from_world(world: &mut World) -> Self {
Self {
cameras: world.query(),
}
}
}
impl ViewNode for MsaaWritebackNode {
type ViewQuery = (
&'static ViewTarget,
&'static MsaaWritebackBlitPipeline,
&'static Msaa,
);

impl Node for MsaaWritebackNode {
fn update(&mut self, world: &mut World) {
self.cameras.update_archetypes(world);
}

fn run(
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(target, blit_pipeline_id, msaa): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
if *world.resource::<Msaa>() == Msaa::Off {
if *msaa == Msaa::Off {
return Ok(());
}

let view_entity = graph.view_entity();
if let Ok((target, blit_pipeline_id)) = self.cameras.get_manual(world, view_entity) {
let blit_pipeline = world.resource::<BlitPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();
let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
return Ok(());
};

// The current "main texture" needs to be bound as an input resource, and we need the "other"
// unused target to be the "resolve target" for the MSAA write. Therefore this is the same
// as a post process write!
let post_process = target.post_process_write();
let blit_pipeline = world.resource::<BlitPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();
let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
return Ok(());
};

let pass_descriptor = RenderPassDescriptor {
label: Some("msaa_writeback"),
// The target's "resolve target" is the "destination" in post_process.
// We will indirectly write the results to the "destination" using
// the MSAA resolve step.
color_attachments: &[Some(RenderPassColorAttachment {
// If MSAA is enabled, then the sampled texture will always exist
view: target.sampled_main_texture_view().unwrap(),
resolve_target: Some(post_process.destination),
ops: Operations {
load: LoadOp::Clear(LinearRgba::BLACK.into()),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
};
// The current "main texture" needs to be bound as an input resource, and we need the "other"
// unused target to be the "resolve target" for the MSAA write. Therefore this is the same
// as a post process write!
let post_process = target.post_process_write();

let pass_descriptor = RenderPassDescriptor {
label: Some("msaa_writeback"),
// The target's "resolve target" is the "destination" in post_process.
// We will indirectly write the results to the "destination" using
// the MSAA resolve step.
color_attachments: &[Some(RenderPassColorAttachment {
// If MSAA is enabled, then the sampled texture will always exist
view: target.sampled_main_texture_view().unwrap(),
resolve_target: Some(post_process.destination),
ops: Operations {
load: LoadOp::Clear(LinearRgba::BLACK.into()),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
};

let bind_group = render_context.render_device().create_bind_group(
None,
&blit_pipeline.texture_bind_group,
&BindGroupEntries::sequential((post_process.source, &blit_pipeline.sampler)),
);
let bind_group = render_context.render_device().create_bind_group(
None,
&blit_pipeline.texture_bind_group,
&BindGroupEntries::sequential((post_process.source, &blit_pipeline.sampler)),
);

let mut render_pass = render_context
.command_encoder()
.begin_render_pass(&pass_descriptor);
let mut render_pass = render_context
.command_encoder()
.begin_render_pass(&pass_descriptor);

render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..3, 0..1);
}
render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..3, 0..1);

Ok(())
}
Expand All @@ -127,10 +126,9 @@ fn prepare_msaa_writeback_pipelines(
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
blit_pipeline: Res<BlitPipeline>,
view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera)>,
msaa: Res<Msaa>,
view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,
) {
for (entity, view_target, camera) in view_targets.iter() {
for (entity, view_target, camera, msaa) in view_targets.iter() {
// only do writeback if writeback is enabled for the camera and this isn't the first camera in the target,
// as there is nothing to write back for the first camera.
if msaa.samples() > 1 && camera.msaa_writeback && camera.sorted_camera_index_for_target > 0
Expand Down
Loading