Move Msaa to component (#14273)

Switches `Msaa` from being a globally configured resource to a per
camera view component.

Closes #7194

# Objective

Allow individual views to describe their own MSAA settings. For example,
when rendering to different windows or to different parts of the same
view.

## Solution

Make `Msaa` a component that is required on all camera bundles.

## Testing

Ran a variety of examples to ensure that nothing broke.

TODO:
- [ ] Make sure android still works per previous comment in
`extract_windows`.

---

## Migration Guide

`Msaa` is no longer configured as a global resource, and should be
specified on each spawned camera if a non-default setting is desired.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
This commit is contained in:
charlotte 2024-07-22 11:28:23 -07:00 committed by GitHub
parent 462da1e49d
commit 03fd1b46ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 222 additions and 218 deletions

View File

@ -2,6 +2,7 @@ use crate::core_2d::graph::Core2d;
use crate::tonemapping::{DebandDither, Tonemapping}; use crate::tonemapping::{DebandDither, Tonemapping};
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::prelude::Msaa;
use bevy_render::{ use bevy_render::{
camera::{ camera::{
Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph, Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph,
@ -35,6 +36,7 @@ pub struct Camera2dBundle {
pub tonemapping: Tonemapping, pub tonemapping: Tonemapping,
pub deband_dither: DebandDither, pub deband_dither: DebandDither,
pub main_texture_usages: CameraMainTextureUsages, pub main_texture_usages: CameraMainTextureUsages,
pub msaa: Msaa,
} }
impl Default for Camera2dBundle { impl Default for Camera2dBundle {
@ -58,6 +60,7 @@ impl Default for Camera2dBundle {
tonemapping: Tonemapping::None, tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled, deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(), main_texture_usages: Default::default(),
msaa: Default::default(),
} }
} }
} }
@ -90,6 +93,7 @@ impl Camera2dBundle {
tonemapping: Tonemapping::None, tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled, deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(), main_texture_usages: Default::default(),
msaa: Default::default(),
} }
} }
} }

View File

@ -4,6 +4,7 @@ use crate::{
}; };
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::view::Msaa;
use bevy_render::{ use bevy_render::{
camera::{Camera, CameraMainTextureUsages, CameraRenderGraph, Exposure, Projection}, camera::{Camera, CameraMainTextureUsages, CameraRenderGraph, Exposure, Projection},
extract_component::ExtractComponent, extract_component::ExtractComponent,
@ -152,6 +153,7 @@ pub struct Camera3dBundle {
pub color_grading: ColorGrading, pub color_grading: ColorGrading,
pub exposure: Exposure, pub exposure: Exposure,
pub main_texture_usages: CameraMainTextureUsages, 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 // NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
@ -171,6 +173,7 @@ impl Default for Camera3dBundle {
exposure: Default::default(), exposure: Default::default(),
main_texture_usages: Default::default(), main_texture_usages: Default::default(),
deband_dither: DebandDither::Enabled, deband_dither: DebandDither::Enabled,
msaa: Default::default(),
} }
} }
} }

View File

@ -610,16 +610,21 @@ pub fn extract_camera_prepass_phase(
pub fn prepare_core_3d_depth_textures( pub fn prepare_core_3d_depth_textures(
mut commands: Commands, mut commands: Commands,
mut texture_cache: ResMut<TextureCache>, mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>, opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>, alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>, transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>, 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(); 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) if !opaque_3d_phases.contains_key(&view)
|| !alpha_mask_3d_phases.contains_key(&view) || !alpha_mask_3d_phases.contains_key(&view)
|| !transmissive_3d_phases.contains_key(&view) || !transmissive_3d_phases.contains_key(&view)
@ -641,13 +646,13 @@ pub fn prepare_core_3d_depth_textures(
} }
let mut textures = HashMap::default(); 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 { let Some(physical_target_size) = camera.physical_target_size else {
continue; continue;
}; };
let cached_texture = textures let cached_texture = textures
.entry(camera.target.clone()) .entry((camera.target.clone(), msaa))
.or_insert_with(|| { .or_insert_with(|| {
// The size of the depth texture // The size of the depth texture
let size = Extent3d { let size = Extent3d {
@ -779,11 +784,8 @@ pub fn prepare_core_3d_transmission_textures(
} }
// Disable MSAA and warn if using deferred rendering // Disable MSAA and warn if using deferred rendering
pub fn check_msaa( pub fn check_msaa(mut deferred_views: Query<&mut Msaa, (With<Camera>, With<DeferredPrepass>)>) {
mut msaa: ResMut<Msaa>, for mut msaa in deferred_views.iter_mut() {
deferred_views: Query<Entity, (With<Camera>, With<DeferredPrepass>)>,
) {
if !deferred_views.is_empty() {
match *msaa { match *msaa {
Msaa::Off => (), Msaa::Off => (),
_ => { _ => {
@ -799,7 +801,6 @@ pub fn check_msaa(
pub fn prepare_prepass_textures( pub fn prepare_prepass_textures(
mut commands: Commands, mut commands: Commands,
mut texture_cache: ResMut<TextureCache>, mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>, opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>, alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
@ -808,6 +809,7 @@ pub fn prepare_prepass_textures(
views_3d: Query<( views_3d: Query<(
Entity, Entity,
&ExtractedCamera, &ExtractedCamera,
&Msaa,
Has<DepthPrepass>, Has<DepthPrepass>,
Has<NormalPrepass>, Has<NormalPrepass>,
Has<MotionVectorPrepass>, Has<MotionVectorPrepass>,
@ -819,8 +821,15 @@ pub fn prepare_prepass_textures(
let mut deferred_textures = HashMap::default(); let mut deferred_textures = HashMap::default();
let mut deferred_lighting_id_textures = HashMap::default(); let mut deferred_lighting_id_textures = HashMap::default();
let mut motion_vectors_textures = HashMap::default(); let mut motion_vectors_textures = HashMap::default();
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in for (
&views_3d entity,
camera,
msaa,
depth_prepass,
normal_prepass,
motion_vector_prepass,
deferred_prepass,
) in &views_3d
{ {
if !opaque_3d_prepass_phases.contains_key(&entity) if !opaque_3d_prepass_phases.contains_key(&entity)
&& !alpha_mask_3d_prepass_phases.contains_key(&entity) && !alpha_mask_3d_prepass_phases.contains_key(&entity)

View File

@ -515,11 +515,10 @@ impl FromWorld for DepthOfFieldGlobalBindGroupLayout {
/// specific to each view. /// specific to each view.
pub fn prepare_depth_of_field_view_bind_group_layouts( pub fn prepare_depth_of_field_view_bind_group_layouts(
mut commands: Commands, mut commands: Commands,
view_targets: Query<(Entity, &DepthOfFieldSettings)>, view_targets: Query<(Entity, &DepthOfFieldSettings, &Msaa)>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>, 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. // Create the bind group layout for the passes that take one input.
let single_input = render_device.create_bind_group_layout( let single_input = render_device.create_bind_group_layout(
Some("depth of field bind group layout (single input)"), Some("depth of field bind group layout (single input)"),
@ -646,16 +645,16 @@ pub fn prepare_depth_of_field_pipelines(
mut commands: Commands, mut commands: Commands,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<DepthOfFieldPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<DepthOfFieldPipeline>>,
msaa: Res<Msaa>,
global_bind_group_layout: Res<DepthOfFieldGlobalBindGroupLayout>, global_bind_group_layout: Res<DepthOfFieldGlobalBindGroupLayout>,
view_targets: Query<( view_targets: Query<(
Entity, Entity,
&ExtractedView, &ExtractedView,
&DepthOfFieldSettings, &DepthOfFieldSettings,
&ViewDepthOfFieldBindGroupLayouts, &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 { let dof_pipeline = DepthOfFieldPipeline {
view_bind_group_layouts: view_bind_group_layouts.clone(), view_bind_group_layouts: view_bind_group_layouts.clone(),
global_bind_group_layout: global_bind_group_layout.layout.clone(), global_bind_group_layout: global_bind_group_layout.layout.clone(),

View File

@ -27,12 +27,13 @@ impl ViewNode for MotionBlurNode {
&'static MotionBlurPipelineId, &'static MotionBlurPipelineId,
&'static ViewPrepassTextures, &'static ViewPrepassTextures,
&'static MotionBlur, &'static MotionBlur,
&'static Msaa,
); );
fn run( fn run(
&self, &self,
_graph: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext, 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, world: &World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
if settings.samples == 0 || settings.shutter_angle <= 0.0 { if settings.samples == 0 || settings.shutter_angle <= 0.0 {
@ -60,7 +61,6 @@ impl ViewNode for MotionBlurNode {
let post_process = view_target.post_process_write(); let post_process = view_target.post_process_write();
let msaa = world.resource::<Msaa>();
let layout = if msaa.samples() == 1 { let layout = if msaa.samples() == 1 {
&motion_blur_pipeline.layout &motion_blur_pipeline.layout
} else { } else {

View File

@ -153,10 +153,9 @@ pub(crate) fn prepare_motion_blur_pipelines(
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<MotionBlurPipeline>>,
pipeline: Res<MotionBlurPipeline>, pipeline: Res<MotionBlurPipeline>,
msaa: Res<Msaa>, views: Query<(Entity, &ExtractedView, &Msaa), With<MotionBlur>>,
views: Query<(Entity, &ExtractedView), With<MotionBlur>>,
) { ) {
for (entity, view) in &views { for (entity, view, msaa) in &views {
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,

View File

@ -6,9 +6,11 @@ use crate::{
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_color::LinearRgba; use bevy_color::LinearRgba;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryItem;
use bevy_render::render_graph::{ViewNode, ViewNodeRunner};
use bevy_render::{ use bevy_render::{
camera::ExtractedCamera, camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphApp, RenderGraphContext}, render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext},
renderer::RenderContext, renderer::RenderContext,
view::{Msaa, ViewTarget}, view::{Msaa, ViewTarget},
Render, RenderSet, Render, RenderSet,
@ -30,90 +32,87 @@ impl Plugin for MsaaWritebackPlugin {
); );
{ {
render_app 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); .add_render_graph_edge(Core2d, Node2d::MsaaWriteback, Node2d::StartMainPass);
} }
{ {
render_app 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); .add_render_graph_edge(Core3d, Node3d::MsaaWriteback, Node3d::StartMainPass);
} }
} }
} }
pub struct MsaaWritebackNode { #[derive(Default)]
cameras: QueryState<(&'static ViewTarget, &'static MsaaWritebackBlitPipeline)>, pub struct MsaaWritebackNode;
}
impl FromWorld for MsaaWritebackNode { impl ViewNode for MsaaWritebackNode {
fn from_world(world: &mut World) -> Self { type ViewQuery = (
Self { &'static ViewTarget,
cameras: world.query(), &'static MsaaWritebackBlitPipeline,
} &'static Msaa,
} );
}
impl Node for MsaaWritebackNode { fn run<'w>(
fn update(&mut self, world: &mut World) {
self.cameras.update_archetypes(world);
}
fn run(
&self, &self,
graph: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext, render_context: &mut RenderContext<'w>,
world: &World, (target, blit_pipeline_id, msaa): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
if *world.resource::<Msaa>() == Msaa::Off { if *msaa == Msaa::Off {
return Ok(()); return Ok(());
} }
let view_entity = graph.view_entity(); let blit_pipeline = world.resource::<BlitPipeline>();
if let Ok((target, blit_pipeline_id)) = self.cameras.get_manual(world, view_entity) { let pipeline_cache = world.resource::<PipelineCache>();
let blit_pipeline = world.resource::<BlitPipeline>(); let Some(pipeline) = pipeline_cache.get_render_pipeline(blit_pipeline_id.0) else {
let pipeline_cache = world.resource::<PipelineCache>(); return Ok(());
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" // 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 // unused target to be the "resolve target" for the MSAA write. Therefore this is the same
// as a post process write! // as a post process write!
let post_process = target.post_process_write(); let post_process = target.post_process_write();
let pass_descriptor = RenderPassDescriptor { let pass_descriptor = RenderPassDescriptor {
label: Some("msaa_writeback"), label: Some("msaa_writeback"),
// The target's "resolve target" is the "destination" in post_process. // The target's "resolve target" is the "destination" in post_process.
// We will indirectly write the results to the "destination" using // We will indirectly write the results to the "destination" using
// the MSAA resolve step. // the MSAA resolve step.
color_attachments: &[Some(RenderPassColorAttachment { color_attachments: &[Some(RenderPassColorAttachment {
// If MSAA is enabled, then the sampled texture will always exist // If MSAA is enabled, then the sampled texture will always exist
view: target.sampled_main_texture_view().unwrap(), view: target.sampled_main_texture_view().unwrap(),
resolve_target: Some(post_process.destination), resolve_target: Some(post_process.destination),
ops: Operations { ops: Operations {
load: LoadOp::Clear(LinearRgba::BLACK.into()), load: LoadOp::Clear(LinearRgba::BLACK.into()),
store: StoreOp::Store, store: StoreOp::Store,
}, },
})], })],
depth_stencil_attachment: None, depth_stencil_attachment: None,
timestamp_writes: None, timestamp_writes: None,
occlusion_query_set: None, occlusion_query_set: None,
}; };
let bind_group = render_context.render_device().create_bind_group( let bind_group = render_context.render_device().create_bind_group(
None, None,
&blit_pipeline.texture_bind_group, &blit_pipeline.texture_bind_group,
&BindGroupEntries::sequential((post_process.source, &blit_pipeline.sampler)), &BindGroupEntries::sequential((post_process.source, &blit_pipeline.sampler)),
); );
let mut render_pass = render_context let mut render_pass = render_context
.command_encoder() .command_encoder()
.begin_render_pass(&pass_descriptor); .begin_render_pass(&pass_descriptor);
render_pass.set_pipeline(pipeline); render_pass.set_pipeline(pipeline);
render_pass.set_bind_group(0, &bind_group, &[]); render_pass.set_bind_group(0, &bind_group, &[]);
render_pass.draw(0..3, 0..1); render_pass.draw(0..3, 0..1);
}
Ok(()) Ok(())
} }
@ -127,10 +126,9 @@ fn prepare_msaa_writeback_pipelines(
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
blit_pipeline: Res<BlitPipeline>, blit_pipeline: Res<BlitPipeline>,
view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera)>, view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>,
msaa: Res<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, // 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. // 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 if msaa.samples() > 1 && camera.msaa_writeback && camera.sorted_camera_index_for_target > 0

View File

@ -245,10 +245,9 @@ fn prepare_skybox_pipelines(
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPipeline>>,
pipeline: Res<SkyboxPipeline>, pipeline: Res<SkyboxPipeline>,
msaa: Res<Msaa>, views: Query<(Entity, &ExtractedView, &Msaa), With<Skybox>>,
views: Query<(Entity, &ExtractedView), With<Skybox>>,
) { ) {
for (entity, view) in &views { for (entity, view, msaa) in &views {
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&pipeline_cache, &pipeline_cache,
&pipeline, &pipeline,

View File

@ -116,11 +116,10 @@ pub fn prepare_skybox_prepass_pipelines(
mut commands: Commands, mut commands: Commands,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPrepassPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPrepassPipeline>>,
msaa: Res<Msaa>,
pipeline: Res<SkyboxPrepassPipeline>, pipeline: Res<SkyboxPrepassPipeline>,
views: Query<(Entity, Has<NormalPrepass>), (With<Skybox>, With<MotionVectorPrepass>)>, views: Query<(Entity, Has<NormalPrepass>, &Msaa), (With<Skybox>, With<MotionVectorPrepass>)>,
) { ) {
for (entity, normal_prepass) in &views { for (entity, normal_prepass, msaa) in &views {
let pipeline_key = SkyboxPrepassPipelineKey { let pipeline_key = SkyboxPrepassPipelineKey {
samples: msaa.samples(), samples: msaa.samples(),
normal_prepass, normal_prepass,

View File

@ -34,6 +34,7 @@ use bevy_render::{
view::{ExtractedView, Msaa, ViewTarget}, view::{ExtractedView, Msaa, ViewTarget},
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
}; };
use bevy_utils::tracing::warn;
const TAA_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(656865235226276); const TAA_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(656865235226276);
@ -46,8 +47,7 @@ impl Plugin for TemporalAntiAliasPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl); load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl);
app.insert_resource(Msaa::Off) app.register_type::<TemporalAntiAliasSettings>();
.register_type::<TemporalAntiAliasSettings>();
let Some(render_app) = app.get_sub_app_mut(RenderApp) else { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return; return;
@ -162,17 +162,23 @@ impl ViewNode for TemporalAntiAliasNode {
&'static TemporalAntiAliasHistoryTextures, &'static TemporalAntiAliasHistoryTextures,
&'static ViewPrepassTextures, &'static ViewPrepassTextures,
&'static TemporalAntiAliasPipelineId, &'static TemporalAntiAliasPipelineId,
&'static Msaa,
); );
fn run( fn run(
&self, &self,
_graph: &mut RenderGraphContext, _graph: &mut RenderGraphContext,
render_context: &mut RenderContext, render_context: &mut RenderContext,
(camera, view_target, taa_history_textures, prepass_textures, taa_pipeline_id): QueryItem< (camera, view_target, taa_history_textures, prepass_textures, taa_pipeline_id, msaa): QueryItem<
Self::ViewQuery, Self::ViewQuery,
>, >,
world: &World, world: &World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
if *msaa != Msaa::Off {
warn!("Temporal anti-aliasing requires MSAA to be disabled");
return Ok(());
}
let (Some(pipelines), Some(pipeline_cache)) = ( let (Some(pipelines), Some(pipeline_cache)) = (
world.get_resource::<TaaPipeline>(), world.get_resource::<TaaPipeline>(),
world.get_resource::<PipelineCache>(), world.get_resource::<PipelineCache>(),

View File

@ -255,15 +255,14 @@ fn queue_line_gizmos_2d(
pipeline: Res<LineGizmoPipeline>, pipeline: Res<LineGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &ExtractedView, Option<&RenderLayers>)>, mut views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>,
) { ) {
let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap(); let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap();
for (view_entity, view, render_layers) in &mut views { for (view_entity, view, msaa, render_layers) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };
@ -309,18 +308,17 @@ fn queue_line_joint_gizmos_2d(
pipeline: Res<LineJointGizmoPipeline>, pipeline: Res<LineJointGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &ExtractedView, Option<&RenderLayers>)>, mut views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>,
) { ) {
let draw_function = draw_functions let draw_function = draw_functions
.read() .read()
.get_id::<DrawLineJointGizmo2d>() .get_id::<DrawLineJointGizmo2d>()
.unwrap(); .unwrap();
for (view_entity, view, render_layers) in &mut views { for (view_entity, view, msaa, render_layers) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };

View File

@ -280,13 +280,13 @@ fn queue_line_gizmos_3d(
pipeline: Res<LineGizmoPipeline>, pipeline: Res<LineGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<( mut views: Query<(
Entity, Entity,
&ExtractedView, &ExtractedView,
&Msaa,
Option<&RenderLayers>, Option<&RenderLayers>,
( (
Has<NormalPrepass>, Has<NormalPrepass>,
@ -301,6 +301,7 @@ fn queue_line_gizmos_3d(
for ( for (
view_entity, view_entity,
view, view,
msaa,
render_layers, render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
) in &mut views ) in &mut views
@ -368,13 +369,13 @@ fn queue_line_joint_gizmos_3d(
pipeline: Res<LineJointGizmoPipeline>, pipeline: Res<LineJointGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>, line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>, line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<( mut views: Query<(
Entity, Entity,
&ExtractedView, &ExtractedView,
&Msaa,
Option<&RenderLayers>, Option<&RenderLayers>,
( (
Has<NormalPrepass>, Has<NormalPrepass>,
@ -392,6 +393,7 @@ fn queue_line_joint_gizmos_3d(
for ( for (
view_entity, view_entity,
view, view,
msaa,
render_layers, render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
) in &mut views ) in &mut views

View File

@ -536,7 +536,6 @@ pub fn queue_material_meshes<M: Material>(
material_pipeline: Res<MaterialPipeline<M>>, material_pipeline: Res<MaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>, mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>, render_meshes: Res<RenderAssets<RenderMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>, render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_mesh_instances: Res<RenderMeshInstances>, render_mesh_instances: Res<RenderMeshInstances>,
@ -551,6 +550,7 @@ pub fn queue_material_meshes<M: Material>(
Entity, Entity,
&ExtractedView, &ExtractedView,
&VisibleEntities, &VisibleEntities,
&Msaa,
Option<&Tonemapping>, Option<&Tonemapping>,
Option<&DebandDither>, Option<&DebandDither>,
Option<&ShadowFilteringMethod>, Option<&ShadowFilteringMethod>,
@ -576,6 +576,7 @@ pub fn queue_material_meshes<M: Material>(
view_entity, view_entity,
view, view,
visible_entities, visible_entities,
msaa,
tonemapping, tonemapping,
dither, dither,
shadow_filter_method, shadow_filter_method,
@ -691,9 +692,14 @@ pub fn queue_material_meshes<M: Material>(
continue; continue;
}; };
let mut mesh_pipeline_key_bits = material.properties.mesh_pipeline_key_bits;
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(
material.properties.alpha_mode,
msaa,
));
let mut mesh_key = view_key let mut mesh_key = view_key
| MeshPipelineKey::from_bits_retain(mesh.key_bits.bits()) | MeshPipelineKey::from_bits_retain(mesh.key_bits.bits())
| material.properties.mesh_pipeline_key_bits; | mesh_pipeline_key_bits;
let lightmap_image = render_lightmaps let lightmap_image = render_lightmaps
.render_lightmaps .render_lightmaps
@ -906,12 +912,11 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
SRes<FallbackImage>, SRes<FallbackImage>,
SRes<MaterialPipeline<M>>, SRes<MaterialPipeline<M>>,
SRes<DefaultOpaqueRendererMethod>, SRes<DefaultOpaqueRendererMethod>,
SRes<Msaa>,
); );
fn prepare_asset( fn prepare_asset(
material: Self::SourceAsset, material: Self::SourceAsset,
(render_device, images, fallback_image, pipeline, default_opaque_render_method, msaa): &mut SystemParamItem<Self::Param>, (render_device, images, fallback_image, pipeline, default_opaque_render_method): &mut SystemParamItem<Self::Param>,
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> { ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
match material.as_bind_group( match material.as_bind_group(
&pipeline.material_layout, &pipeline.material_layout,
@ -930,7 +935,6 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE, MeshPipelineKey::READS_VIEW_TRANSMISSION_TEXTURE,
material.reads_view_transmission_texture(), material.reads_view_transmission_texture(),
); );
mesh_pipeline_key_bits.insert(alpha_mode_pipeline_key(material.alpha_mode(), msaa));
Ok(PreparedMaterial { Ok(PreparedMaterial {
bindings: prepared.bindings, bindings: prepared.bindings,

View File

@ -172,7 +172,6 @@ impl Plugin for MeshletPlugin {
app.init_asset::<MeshletMesh>() app.init_asset::<MeshletMesh>()
.register_asset_loader(MeshletMeshSaverLoader) .register_asset_loader(MeshletMeshSaverLoader)
.insert_resource(Msaa::Off)
.add_systems( .add_systems(
PostUpdate, PostUpdate,
check_visibility::<WithMeshletMesh>.in_set(VisibilitySystems::CheckVisibility), check_visibility::<WithMeshletMesh>.in_set(VisibilitySystems::CheckVisibility),
@ -282,15 +281,20 @@ fn configure_meshlet_views(
mut views_3d: Query<( mut views_3d: Query<(
Entity, Entity,
&mut Camera3d, &mut Camera3d,
&Msaa,
Has<NormalPrepass>, Has<NormalPrepass>,
Has<MotionVectorPrepass>, Has<MotionVectorPrepass>,
Has<DeferredPrepass>, Has<DeferredPrepass>,
)>, )>,
mut commands: Commands, mut commands: Commands,
) { ) {
for (entity, mut camera_3d, normal_prepass, motion_vector_prepass, deferred_prepass) in for (entity, mut camera_3d, msaa, normal_prepass, motion_vector_prepass, deferred_prepass) in
&mut views_3d &mut views_3d
{ {
if *msaa != Msaa::Off {
panic!("MeshletPlugin can't be used. MSAA is not supported.");
}
let mut usages: TextureUsages = camera_3d.depth_texture_usages.into(); let mut usages: TextureUsages = camera_3d.depth_texture_usages.into();
usages |= TextureUsages::TEXTURE_BINDING; usages |= TextureUsages::TEXTURE_BINDING;
camera_3d.depth_texture_usages = usages.into(); camera_3d.depth_texture_usages = usages.into();

View File

@ -679,7 +679,6 @@ pub fn queue_prepass_material_meshes<M: Material>(
prepass_pipeline: Res<PrepassPipeline<M>>, prepass_pipeline: Res<PrepassPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>, mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>, render_meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderMeshInstances>, render_mesh_instances: Res<RenderMeshInstances>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>, render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
@ -693,6 +692,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
( (
Entity, Entity,
&VisibleEntities, &VisibleEntities,
&Msaa,
Option<&DepthPrepass>, Option<&DepthPrepass>,
Option<&NormalPrepass>, Option<&NormalPrepass>,
Option<&MotionVectorPrepass>, Option<&MotionVectorPrepass>,
@ -722,6 +722,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
for ( for (
view, view,
visible_entities, visible_entities,
msaa,
depth_prepass, depth_prepass,
normal_prepass, normal_prepass,
motion_vector_prepass, motion_vector_prepass,
@ -780,7 +781,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
let alpha_mode = material.properties.alpha_mode; let alpha_mode = material.properties.alpha_mode;
match alpha_mode { match alpha_mode {
AlphaMode::Opaque | AlphaMode::AlphaToCoverage | AlphaMode::Mask(_) => { AlphaMode::Opaque | AlphaMode::AlphaToCoverage | AlphaMode::Mask(_) => {
mesh_key |= alpha_mode_pipeline_key(alpha_mode, &msaa); mesh_key |= alpha_mode_pipeline_key(alpha_mode, msaa);
} }
AlphaMode::Blend AlphaMode::Blend
| AlphaMode::Premultiplied | AlphaMode::Premultiplied

View File

@ -456,6 +456,7 @@ pub fn prepare_mesh_view_bind_groups(
Entity, Entity,
&ViewShadowBindings, &ViewShadowBindings,
&ViewClusterBindings, &ViewClusterBindings,
&Msaa,
Option<&ScreenSpaceAmbientOcclusionTextures>, Option<&ScreenSpaceAmbientOcclusionTextures>,
Option<&ViewPrepassTextures>, Option<&ViewPrepassTextures>,
Option<&ViewTransmissionTexture>, Option<&ViewTransmissionTexture>,
@ -469,7 +470,6 @@ pub fn prepare_mesh_view_bind_groups(
Res<FallbackImage>, Res<FallbackImage>,
Res<FallbackImageZero>, Res<FallbackImageZero>,
), ),
msaa: Res<Msaa>,
globals_buffer: Res<GlobalsBuffer>, globals_buffer: Res<GlobalsBuffer>,
tonemapping_luts: Res<TonemappingLuts>, tonemapping_luts: Res<TonemappingLuts>,
light_probes_buffer: Res<LightProbesBuffer>, light_probes_buffer: Res<LightProbesBuffer>,
@ -501,6 +501,7 @@ pub fn prepare_mesh_view_bind_groups(
entity, entity,
shadow_bindings, shadow_bindings,
cluster_bindings, cluster_bindings,
msaa,
ssao_textures, ssao_textures,
prepass_textures, prepass_textures,
transmission_texture, transmission_texture,

View File

@ -484,17 +484,16 @@ fn extract_ssao_settings(
mut commands: Commands, mut commands: Commands,
cameras: Extract< cameras: Extract<
Query< Query<
(Entity, &Camera, &ScreenSpaceAmbientOcclusionSettings), (Entity, &Camera, &ScreenSpaceAmbientOcclusionSettings, &Msaa),
(With<Camera3d>, With<DepthPrepass>, With<NormalPrepass>), (With<Camera3d>, With<DepthPrepass>, With<NormalPrepass>),
>, >,
>, >,
msaa: Extract<Res<Msaa>>,
) { ) {
for (entity, camera, ssao_settings) in &cameras { for (entity, camera, ssao_settings, msaa) in &cameras {
if **msaa != Msaa::Off { if *msaa != Msaa::Off {
error!( error!(
"SSAO is being used which requires Msaa::Off, but Msaa is currently set to Msaa::{:?}", "SSAO is being used which requires Msaa::Off, but Msaa is currently set to Msaa::{:?}",
**msaa *msaa
); );
return; return;
} }

View File

@ -307,6 +307,7 @@ impl ViewNode for VolumetricFogNode {
Read<ViewVolumetricFog>, Read<ViewVolumetricFog>,
Read<MeshViewBindGroup>, Read<MeshViewBindGroup>,
Read<ViewScreenSpaceReflectionsUniformOffset>, Read<ViewScreenSpaceReflectionsUniformOffset>,
Read<Msaa>,
Read<ViewEnvironmentMapUniformOffset>, Read<ViewEnvironmentMapUniformOffset>,
); );
@ -325,6 +326,7 @@ impl ViewNode for VolumetricFogNode {
view_fog_volumes, view_fog_volumes,
view_bind_group, view_bind_group,
view_ssr_offset, view_ssr_offset,
msaa,
view_environment_map_offset, view_environment_map_offset,
): QueryItem<'w, Self::ViewQuery>, ): QueryItem<'w, Self::ViewQuery>,
world: &'w World, world: &'w World,
@ -333,7 +335,6 @@ impl ViewNode for VolumetricFogNode {
let volumetric_lighting_pipeline = world.resource::<VolumetricFogPipeline>(); let volumetric_lighting_pipeline = world.resource::<VolumetricFogPipeline>();
let volumetric_lighting_uniform_buffers = world.resource::<VolumetricFogUniformBuffer>(); let volumetric_lighting_uniform_buffers = world.resource::<VolumetricFogUniformBuffer>();
let image_assets = world.resource::<RenderAssets<GpuImage>>(); let image_assets = world.resource::<RenderAssets<GpuImage>>();
let msaa = world.resource::<Msaa>();
let mesh_allocator = world.resource::<MeshAllocator>(); let mesh_allocator = world.resource::<MeshAllocator>();
// Fetch the uniform buffer and binding. // Fetch the uniform buffer and binding.
@ -594,6 +595,7 @@ pub fn prepare_volumetric_fog_pipelines(
( (
Entity, Entity,
&ExtractedView, &ExtractedView,
&Msaa,
Has<NormalPrepass>, Has<NormalPrepass>,
Has<DepthPrepass>, Has<DepthPrepass>,
Has<MotionVectorPrepass>, Has<MotionVectorPrepass>,
@ -601,13 +603,19 @@ pub fn prepare_volumetric_fog_pipelines(
), ),
With<VolumetricFogSettings>, With<VolumetricFogSettings>,
>, >,
msaa: Res<Msaa>,
meshes: Res<RenderAssets<RenderMesh>>, meshes: Res<RenderAssets<RenderMesh>>,
) { ) {
let plane_mesh = meshes.get(&PLANE_MESH).expect("Plane mesh not found!"); let plane_mesh = meshes.get(&PLANE_MESH).expect("Plane mesh not found!");
for (entity, view, normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass) in for (
view_targets.iter() entity,
view,
msaa,
normal_prepass,
depth_prepass,
motion_vector_prepass,
deferred_prepass,
) in view_targets.iter()
{ {
// Create a mesh pipeline view layout key corresponding to the view. // Create a mesh pipeline view layout key corresponding to the view.
let mut mesh_pipeline_view_key = MeshPipelineViewLayoutKey::from(*msaa); let mut mesh_pipeline_view_key = MeshPipelineViewLayoutKey::from(*msaa);

View File

@ -5,12 +5,12 @@ use bevy_asset::{load_internal_asset, Handle};
pub use visibility::*; pub use visibility::*;
pub use window::*; pub use window::*;
use crate::extract_component::ExtractComponentPlugin;
use crate::{ use crate::{
camera::{ camera::{
CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera, CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera,
ManualTextureViews, MipBias, TemporalJitter, ManualTextureViews, MipBias, TemporalJitter,
}, },
extract_resource::{ExtractResource, ExtractResourcePlugin},
prelude::Shader, prelude::Shader,
primitives::Frustum, primitives::Frustum,
render_asset::RenderAssets, render_asset::RenderAssets,
@ -28,6 +28,7 @@ use bevy_color::LinearRgba;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_math::{mat3, vec2, vec3, Mat3, Mat4, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles}; use bevy_math::{mat3, vec2, vec3, Mat3, Mat4, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles};
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render_macros::ExtractComponent;
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use std::{ use std::{
@ -107,10 +108,9 @@ impl Plugin for ViewPlugin {
.register_type::<Visibility>() .register_type::<Visibility>()
.register_type::<VisibleEntities>() .register_type::<VisibleEntities>()
.register_type::<ColorGrading>() .register_type::<ColorGrading>()
.init_resource::<Msaa>()
// NOTE: windows.is_changed() handles cases where a window was resized // NOTE: windows.is_changed() handles cases where a window was resized
.add_plugins(( .add_plugins((
ExtractResourcePlugin::<Msaa>::default(), ExtractComponentPlugin::<Msaa>::default(),
VisibilityPlugin, VisibilityPlugin,
VisibilityRangePlugin, VisibilityRangePlugin,
)); ));
@ -139,24 +139,26 @@ impl Plugin for ViewPlugin {
/// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). /// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing).
/// ///
/// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// The number of samples to run for Multi-Sample Anti-Aliasing for a given camera. Higher numbers
/// smoother edges. /// result in smoother edges.
/// Defaults to 4 samples. ///
/// Defaults to 4 samples. Some advanced rendering features may require that MSAA be disabled.
/// ///
/// Note that web currently only supports 1 or 4 samples. /// Note that web currently only supports 1 or 4 samples.
///
/// # Example
/// ```
/// # use bevy_app::prelude::App;
/// # use bevy_render::prelude::Msaa;
/// App::new()
/// .insert_resource(Msaa::default())
/// .run();
/// ```
#[derive( #[derive(
Resource, Default, Clone, Copy, ExtractResource, Reflect, PartialEq, PartialOrd, Eq, Hash, Debug, Component,
Default,
Clone,
Copy,
ExtractComponent,
Reflect,
PartialEq,
PartialOrd,
Eq,
Hash,
Debug,
)] )]
#[reflect(Resource, Default)] #[reflect(Component, Default)]
pub enum Msaa { pub enum Msaa {
Off = 1, Off = 1,
Sample2 = 2, Sample2 = 2,
@ -797,7 +799,6 @@ pub fn prepare_view_targets(
mut commands: Commands, mut commands: Commands,
windows: Res<ExtractedWindows>, windows: Res<ExtractedWindows>,
images: Res<RenderAssets<GpuImage>>, images: Res<RenderAssets<GpuImage>>,
msaa: Res<Msaa>,
clear_color_global: Res<ClearColor>, clear_color_global: Res<ClearColor>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
mut texture_cache: ResMut<TextureCache>, mut texture_cache: ResMut<TextureCache>,
@ -806,12 +807,13 @@ pub fn prepare_view_targets(
&ExtractedCamera, &ExtractedCamera,
&ExtractedView, &ExtractedView,
&CameraMainTextureUsages, &CameraMainTextureUsages,
&Msaa,
)>, )>,
manual_texture_views: Res<ManualTextureViews>, manual_texture_views: Res<ManualTextureViews>,
) { ) {
let mut textures = HashMap::default(); let mut textures = HashMap::default();
let mut output_textures = HashMap::default(); let mut output_textures = HashMap::default();
for (entity, camera, view, texture_usage) in cameras.iter() { for (entity, camera, view, texture_usage, msaa) in cameras.iter() {
let (Some(target_size), Some(target)) = (camera.physical_target_size, &camera.target) let (Some(target_size), Some(target)) = (camera.physical_target_size, &camera.target)
else { else {
continue; continue;
@ -847,7 +849,7 @@ pub fn prepare_view_targets(
}; };
let (a, b, sampled, main_texture) = textures let (a, b, sampled, main_texture) = textures
.entry((camera.target.clone(), view.hdr)) .entry((camera.target.clone(), view.hdr, msaa))
.or_insert_with(|| { .or_insert_with(|| {
let descriptor = TextureDescriptor { let descriptor = TextureDescriptor {
label: None, label: None,

View File

@ -30,8 +30,6 @@ use screenshot::{
ScreenshotManager, ScreenshotPlugin, ScreenshotPreparedState, ScreenshotToScreenPipeline, ScreenshotManager, ScreenshotPlugin, ScreenshotPreparedState, ScreenshotToScreenPipeline,
}; };
use super::Msaa;
pub struct WindowRenderPlugin; pub struct WindowRenderPlugin;
impl Plugin for WindowRenderPlugin { impl Plugin for WindowRenderPlugin {
@ -250,11 +248,9 @@ pub fn prepare_windows(
mut windows: ResMut<ExtractedWindows>, mut windows: ResMut<ExtractedWindows>,
mut window_surfaces: ResMut<WindowSurfaces>, mut window_surfaces: ResMut<WindowSurfaces>,
render_device: Res<RenderDevice>, render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
screenshot_pipeline: Res<ScreenshotToScreenPipeline>, screenshot_pipeline: Res<ScreenshotToScreenPipeline>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<ScreenshotToScreenPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<ScreenshotToScreenPipeline>>,
mut msaa: ResMut<Msaa>,
#[cfg(target_os = "linux")] render_instance: Res<RenderInstance>, #[cfg(target_os = "linux")] render_instance: Res<RenderInstance>,
) { ) {
for window in windows.windows.values_mut() { for window in windows.windows.values_mut() {
@ -263,35 +259,6 @@ pub fn prepare_windows(
continue; continue;
}; };
// This is an ugly hack to work around drivers that don't support MSAA.
// This should be removed once https://github.com/bevyengine/bevy/issues/7194 lands and we're doing proper
// feature detection for MSAA.
// When removed, we can also remove the `.after(prepare_windows)` of `prepare_core_3d_depth_textures` and `prepare_prepass_textures`
let sample_flags = render_adapter
.get_texture_format_features(surface_data.configuration.format)
.flags;
if !sample_flags.sample_count_supported(msaa.samples()) {
let fallback = if sample_flags.sample_count_supported(Msaa::default().samples()) {
Msaa::default()
} else {
Msaa::Off
};
let fallback_str = if fallback == Msaa::Off {
"disabling MSAA".to_owned()
} else {
format!("MSAA {}x", fallback.samples())
};
bevy_utils::tracing::warn!(
"MSAA {}x is not supported on this device. Falling back to {}.",
msaa.samples(),
fallback_str,
);
*msaa = fallback;
}
// A recurring issue is hitting `wgpu::SurfaceError::Timeout` on certain Linux // A recurring issue is hitting `wgpu::SurfaceError::Timeout` on certain Linux
// mesa driver implementations. This seems to be a quirk of some drivers. // mesa driver implementations. This seems to be a quirk of some drivers.
// We'd rather keep panicking when not on Linux mesa, because in those case, // We'd rather keep panicking when not on Linux mesa, because in those case,

View File

@ -369,7 +369,6 @@ pub fn queue_material2d_meshes<M: Material2d>(
material2d_pipeline: Res<Material2dPipeline<M>>, material2d_pipeline: Res<Material2dPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>, mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>, render_meshes: Res<RenderAssets<RenderMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>, render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>,
mut render_mesh_instances: ResMut<RenderMesh2dInstances>, mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
@ -379,6 +378,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
Entity, Entity,
&ExtractedView, &ExtractedView,
&VisibleEntities, &VisibleEntities,
&Msaa,
Option<&Tonemapping>, Option<&Tonemapping>,
Option<&DebandDither>, Option<&DebandDither>,
)>, )>,
@ -389,7 +389,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
return; return;
} }
for (view_entity, view, visible_entities, tonemapping, dither) in &mut views { for (view_entity, view, visible_entities, msaa, tonemapping, dither) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };

View File

@ -472,26 +472,25 @@ pub fn queue_sprites(
sprite_pipeline: Res<SpritePipeline>, sprite_pipeline: Res<SpritePipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
extracted_sprites: Res<ExtractedSprites>, extracted_sprites: Res<ExtractedSprites>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<( mut views: Query<(
Entity, Entity,
&VisibleEntities, &VisibleEntities,
&ExtractedView, &ExtractedView,
&Msaa,
Option<&Tonemapping>, Option<&Tonemapping>,
Option<&DebandDither>, Option<&DebandDither>,
)>, )>,
) { ) {
let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples());
let draw_sprite_function = draw_functions.read().id::<DrawSprite>(); let draw_sprite_function = draw_functions.read().id::<DrawSprite>();
for (view_entity, visible_entities, view, tonemapping, dither) in &mut views { for (view_entity, visible_entities, view, msaa, tonemapping, dither) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };
let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples());
let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key; let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key;
if !view.hdr { if !view.hdr {

View File

@ -351,17 +351,16 @@ pub fn queue_colored_mesh2d(
colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>, colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>, render_meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderColoredMesh2dInstances>, render_mesh_instances: Res<RenderColoredMesh2dInstances>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &VisibleEntities, &ExtractedView)>, mut views: Query<(Entity, &VisibleEntities, &ExtractedView, &Msaa)>,
) { ) {
if render_mesh_instances.is_empty() { if render_mesh_instances.is_empty() {
return; return;
} }
// Iterate each view (a camera is a view) // Iterate each view (a camera is a view)
for (view_entity, visible_entities, view) in &mut views { for (view_entity, visible_entities, view, msaa) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };

View File

@ -29,7 +29,6 @@ const HIGH_RES_LAYERS: RenderLayers = RenderLayers::layer(1);
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.insert_resource(Msaa::Off)
.add_systems(Startup, (setup_camera, setup_sprite, setup_mesh)) .add_systems(Startup, (setup_camera, setup_sprite, setup_mesh))
.add_systems(Update, (rotate, fit_canvas)) .add_systems(Update, (rotate, fit_canvas))
.run(); .run();
@ -131,6 +130,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
target: RenderTarget::Image(image_handle.clone()), target: RenderTarget::Image(image_handle.clone()),
..default() ..default()
}, },
msaa: Msaa::Off,
..default() ..default()
}, },
InGameCamera, InGameCamera,
@ -149,7 +149,14 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
// the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen.
// here, the canvas and one of the sample sprites will be rendered by this camera // here, the canvas and one of the sample sprites will be rendered by this camera
commands.spawn((Camera2dBundle::default(), OuterCamera, HIGH_RES_LAYERS)); commands.spawn((
Camera2dBundle {
msaa: Msaa::Off,
..default()
},
OuterCamera,
HIGH_RES_LAYERS,
));
} }
/// Rotates entities to demonstrate grid snapping. /// Rotates entities to demonstrate grid snapping.

View File

@ -23,7 +23,6 @@ use bevy::{
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa::Off)
.add_plugins((DefaultPlugins, TemporalAntiAliasPlugin)) .add_plugins((DefaultPlugins, TemporalAntiAliasPlugin))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, (modify_aa, modify_sharpening, update_ui)) .add_systems(Update, (modify_aa, modify_sharpening, update_ui))
@ -38,13 +37,13 @@ fn modify_aa(
Option<&mut Fxaa>, Option<&mut Fxaa>,
Option<&mut SmaaSettings>, Option<&mut SmaaSettings>,
Option<&TemporalAntiAliasSettings>, Option<&TemporalAntiAliasSettings>,
&mut Msaa,
), ),
With<Camera>, With<Camera>,
>, >,
mut msaa: ResMut<Msaa>,
mut commands: Commands, mut commands: Commands,
) { ) {
let (camera_entity, fxaa, smaa, taa) = camera.single_mut(); let (camera_entity, fxaa, smaa, taa, mut msaa) = camera.single_mut();
let mut camera = commands.entity(camera_entity); let mut camera = commands.entity(camera_entity);
// No AA // No AA
@ -176,13 +175,13 @@ fn update_ui(
Option<&SmaaSettings>, Option<&SmaaSettings>,
Option<&TemporalAntiAliasSettings>, Option<&TemporalAntiAliasSettings>,
&ContrastAdaptiveSharpeningSettings, &ContrastAdaptiveSharpeningSettings,
&Msaa,
), ),
With<Camera>, With<Camera>,
>, >,
msaa: Res<Msaa>,
mut ui: Query<&mut Text>, mut ui: Query<&mut Text>,
) { ) {
let (fxaa, smaa, taa, cas_settings) = camera.single(); let (fxaa, smaa, taa, cas_settings, msaa) = camera.single();
let mut ui = ui.single_mut(); let mut ui = ui.single_mut();
let ui = &mut ui.sections[0].value; let ui = &mut ui.sections[0].value;

View File

@ -17,7 +17,6 @@ use bevy::{
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa::Off)
.insert_resource(DefaultOpaqueRendererMethod::deferred()) .insert_resource(DefaultOpaqueRendererMethod::deferred())
.insert_resource(DirectionalLightShadowMap { size: 4096 }) .insert_resource(DirectionalLightShadowMap { size: 4096 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
@ -42,6 +41,8 @@ fn setup(
}, },
transform: Transform::from_xyz(0.7, 0.7, 1.0) transform: Transform::from_xyz(0.7, 0.7, 1.0)
.looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
// MSAA needs to be off for Deferred rendering
msaa: Msaa::Off,
..default() ..default()
}, },
FogSettings { FogSettings {

View File

@ -98,7 +98,6 @@ fn main() {
// reflections at this time. Disable multisampled antialiasing, as deferred // reflections at this time. Disable multisampled antialiasing, as deferred
// rendering doesn't support that. // rendering doesn't support that.
App::new() App::new()
.insert_resource(Msaa::Off)
.insert_resource(DefaultOpaqueRendererMethod::deferred()) .insert_resource(DefaultOpaqueRendererMethod::deferred())
.init_resource::<AppSettings>() .init_resource::<AppSettings>()
.add_plugins(DefaultPlugins.set(WindowPlugin { .add_plugins(DefaultPlugins.set(WindowPlugin {
@ -236,6 +235,7 @@ fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
hdr: true, hdr: true,
..default() ..default()
}, },
msaa: Msaa::Off,
..default() ..default()
}) })
.insert(EnvironmentMapLight { .insert(EnvironmentMapLight {

View File

@ -57,8 +57,7 @@ fn main() {
// it _greatly enhances_ the look of the resulting blur effects. // it _greatly enhances_ the look of the resulting blur effects.
// Sadly, it's not available under WebGL. // Sadly, it's not available under WebGL.
#[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))] #[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))]
app.insert_resource(Msaa::Off) app.add_plugins(TemporalAntiAliasPlugin);
.add_plugins(TemporalAntiAliasPlugin);
app.run(); app.run();
} }
@ -352,6 +351,8 @@ fn setup(
}, },
tonemapping: Tonemapping::TonyMcMapface, tonemapping: Tonemapping::TonyMcMapface,
exposure: Exposure { ev100: 6.0 }, exposure: Exposure { ev100: 6.0 },
#[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))]
msaa: Msaa::Off,
..default() ..default()
}, },
#[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))] #[cfg(not(all(feature = "webgl2", target_arch = "wasm32")))]

View File

@ -6,7 +6,6 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.insert_resource(Msaa::default())
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, fade_transparency) .add_systems(Update, fade_transparency)
@ -100,6 +99,7 @@ fn setup(
// Camera // Camera
commands.spawn(Camera3dBundle { commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), transform: Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
msaa: Msaa::Off,
..default() ..default()
}); });
} }

View File

@ -23,14 +23,8 @@ fn main() {
..default() ..default()
})) }))
.add_systems(Startup, (setup_scene, setup_music)) .add_systems(Startup, (setup_scene, setup_music))
.add_systems(Update, (touch_camera, button_handler, handle_lifetime)); .add_systems(Update, (touch_camera, button_handler, handle_lifetime))
.run();
// MSAA makes some Android devices panic, this is under investigation
// https://github.com/bevyengine/bevy/issues/8229
#[cfg(target_os = "android")]
app.insert_resource(Msaa::Off);
app.run();
} }
fn touch_camera( fn touch_camera(
@ -109,6 +103,10 @@ fn setup_scene(
// camera // camera
commands.spawn(Camera3dBundle { commands.spawn(Camera3dBundle {
transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
// MSAA makes some Android devices panic, this is under investigation
// https://github.com/bevyengine/bevy/issues/8229
#[cfg(target_os = "android")]
msaa: Msaa::Off,
..default() ..default()
}); });

View File

@ -233,11 +233,10 @@ fn prepare_custom_phase_item_buffers(mut commands: Commands) {
fn queue_custom_phase_item( fn queue_custom_phase_item(
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
custom_phase_pipeline: Res<CustomPhasePipeline>, custom_phase_pipeline: Res<CustomPhasePipeline>,
msaa: Res<Msaa>,
mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>, mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>, opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
mut specialized_render_pipelines: ResMut<SpecializedRenderPipelines<CustomPhasePipeline>>, mut specialized_render_pipelines: ResMut<SpecializedRenderPipelines<CustomPhasePipeline>>,
views: Query<(Entity, &VisibleEntities), With<ExtractedView>>, views: Query<(Entity, &VisibleEntities, &Msaa), With<ExtractedView>>,
) { ) {
let draw_custom_phase_item = opaque_draw_functions let draw_custom_phase_item = opaque_draw_functions
.read() .read()
@ -246,7 +245,7 @@ fn queue_custom_phase_item(
// Render phases are per-view, so we need to iterate over all views so that // Render phases are per-view, so we need to iterate over all views so that
// the entity appears in them. (In this example, we have only one view, but // the entity appears in them. (In this example, we have only one view, but
// it's good practice to loop over all views anyway.) // it's good practice to loop over all views anyway.)
for (view_entity, view_visible_entities) in views.iter() { for (view_entity, view_visible_entities, msaa) in views.iter() {
let Some(opaque_phase) = opaque_render_phases.get_mut(&view_entity) else { let Some(opaque_phase) = opaque_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };

View File

@ -116,24 +116,23 @@ struct InstanceData {
fn queue_custom( fn queue_custom(
transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>, transparent_3d_draw_functions: Res<DrawFunctions<Transparent3d>>,
custom_pipeline: Res<CustomPipeline>, custom_pipeline: Res<CustomPipeline>,
msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>,
pipeline_cache: Res<PipelineCache>, pipeline_cache: Res<PipelineCache>,
meshes: Res<RenderAssets<RenderMesh>>, meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderMeshInstances>, render_mesh_instances: Res<RenderMeshInstances>,
material_meshes: Query<Entity, With<InstanceMaterialData>>, material_meshes: Query<Entity, With<InstanceMaterialData>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>, mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(Entity, &ExtractedView)>, mut views: Query<(Entity, &ExtractedView, &Msaa)>,
) { ) {
let draw_custom = transparent_3d_draw_functions.read().id::<DrawCustom>(); let draw_custom = transparent_3d_draw_functions.read().id::<DrawCustom>();
let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples()); for (view_entity, view, msaa) in &mut views {
for (view_entity, view) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else { let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue; continue;
}; };
let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr);
let rangefinder = view.rangefinder3d(); let rangefinder = view.rangefinder3d();
for entity in &material_meshes { for entity in &material_meshes {

View File

@ -34,8 +34,6 @@ fn main() {
)) ))
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, (rotate, toggle_prepass_view)) .add_systems(Update, (rotate, toggle_prepass_view))
// Disabling MSAA for maximum compatibility. Shader prepass with MSAA needs GPU capability MULTISAMPLED_SHADING
.insert_resource(Msaa::Off)
.run(); .run();
} }
@ -52,6 +50,8 @@ fn setup(
commands.spawn(( commands.spawn((
Camera3dBundle { Camera3dBundle {
transform: Transform::from_xyz(-2.0, 3., 5.0).looking_at(Vec3::ZERO, Vec3::Y), transform: Transform::from_xyz(-2.0, 3., 5.0).looking_at(Vec3::ZERO, Vec3::Y),
// Disabling MSAA for maximum compatibility. Shader prepass with MSAA needs GPU capability MULTISAMPLED_SHADING
msaa: Msaa::Off,
..default() ..default()
}, },
// To enable the prepass you need to add the components associated with the ones you need // To enable the prepass you need to add the components associated with the ones you need