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 bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use bevy_render::prelude::Msaa;
use bevy_render::{
camera::{
Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph,
@ -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 {
@ -58,6 +60,7 @@ impl Default for Camera2dBundle {
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(),
msaa: Default::default(),
}
}
}
@ -90,6 +93,7 @@ impl Camera2dBundle {
tonemapping: Tonemapping::None,
deband_dither: DebandDither::Disabled,
main_texture_usages: Default::default(),
msaa: Default::default(),
}
}
}

View file

@ -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,
@ -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
@ -171,6 +173,7 @@ impl Default for Camera3dBundle {
exposure: Default::default(),
main_texture_usages: Default::default(),
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(
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)
@ -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 {
@ -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 => (),
_ => {
@ -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>>,
@ -808,6 +809,7 @@ pub fn prepare_prepass_textures(
views_3d: Query<(
Entity,
&ExtractedCamera,
&Msaa,
Has<DepthPrepass>,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
@ -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)

View file

@ -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)"),
@ -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(),

View file

@ -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 {
@ -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 {

View file

@ -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,

View file

@ -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,
@ -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(());
};
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();
// 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 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(())
}
@ -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

View file

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

View file

@ -116,11 +116,10 @@ pub fn prepare_skybox_prepass_pipelines(
mut commands: Commands,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<SkyboxPrepassPipeline>>,
msaa: Res<Msaa>,
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 {
samples: msaa.samples(),
normal_prepass,

View file

@ -34,6 +34,7 @@ use bevy_render::{
view::{ExtractedView, Msaa, ViewTarget},
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
};
use bevy_utils::tracing::warn;
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) {
load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl);
app.insert_resource(Msaa::Off)
.register_type::<TemporalAntiAliasSettings>();
app.register_type::<TemporalAntiAliasSettings>();
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
@ -162,17 +162,23 @@ impl ViewNode for TemporalAntiAliasNode {
&'static TemporalAntiAliasHistoryTextures,
&'static ViewPrepassTextures,
&'static TemporalAntiAliasPipelineId,
&'static Msaa,
);
fn run(
&self,
_graph: &mut RenderGraphContext,
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,
>,
world: &World,
) -> Result<(), NodeRunError> {
if *msaa != Msaa::Off {
warn!("Temporal anti-aliasing requires MSAA to be disabled");
return Ok(());
}
let (Some(pipelines), Some(pipeline_cache)) = (
world.get_resource::<TaaPipeline>(),
world.get_resource::<PipelineCache>(),

View file

@ -255,15 +255,14 @@ fn queue_line_gizmos_2d(
pipeline: Res<LineGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
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();
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 {
continue;
};
@ -309,18 +308,17 @@ fn queue_line_joint_gizmos_2d(
pipeline: Res<LineJointGizmoPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<LineJointGizmoPipeline>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
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::<DrawLineJointGizmo2d>()
.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 {
continue;
};

View file

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

View file

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

View file

@ -172,7 +172,6 @@ impl Plugin for MeshletPlugin {
app.init_asset::<MeshletMesh>()
.register_asset_loader(MeshletMeshSaverLoader)
.insert_resource(Msaa::Off)
.add_systems(
PostUpdate,
check_visibility::<WithMeshletMesh>.in_set(VisibilitySystems::CheckVisibility),
@ -282,15 +281,20 @@ fn configure_meshlet_views(
mut views_3d: Query<(
Entity,
&mut Camera3d,
&Msaa,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
)>,
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
{
if *msaa != Msaa::Off {
panic!("MeshletPlugin can't be used. MSAA is not supported.");
}
let mut usages: TextureUsages = camera_3d.depth_texture_usages.into();
usages |= TextureUsages::TEXTURE_BINDING;
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>>,
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
@ -693,6 +692,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
(
Entity,
&VisibleEntities,
&Msaa,
Option<&DepthPrepass>,
Option<&NormalPrepass>,
Option<&MotionVectorPrepass>,
@ -722,6 +722,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
for (
view,
visible_entities,
msaa,
depth_prepass,
normal_prepass,
motion_vector_prepass,
@ -780,7 +781,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
let alpha_mode = material.properties.alpha_mode;
match alpha_mode {
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::Premultiplied

View file

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

View file

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

View file

@ -307,6 +307,7 @@ impl ViewNode for VolumetricFogNode {
Read<ViewVolumetricFog>,
Read<MeshViewBindGroup>,
Read<ViewScreenSpaceReflectionsUniformOffset>,
Read<Msaa>,
Read<ViewEnvironmentMapUniformOffset>,
);
@ -325,6 +326,7 @@ impl ViewNode for VolumetricFogNode {
view_fog_volumes,
view_bind_group,
view_ssr_offset,
msaa,
view_environment_map_offset,
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
@ -333,7 +335,6 @@ impl ViewNode for VolumetricFogNode {
let volumetric_lighting_pipeline = world.resource::<VolumetricFogPipeline>();
let volumetric_lighting_uniform_buffers = world.resource::<VolumetricFogUniformBuffer>();
let image_assets = world.resource::<RenderAssets<GpuImage>>();
let msaa = world.resource::<Msaa>();
let mesh_allocator = world.resource::<MeshAllocator>();
// Fetch the uniform buffer and binding.
@ -594,6 +595,7 @@ pub fn prepare_volumetric_fog_pipelines(
(
Entity,
&ExtractedView,
&Msaa,
Has<NormalPrepass>,
Has<DepthPrepass>,
Has<MotionVectorPrepass>,
@ -601,13 +603,19 @@ pub fn prepare_volumetric_fog_pipelines(
),
With<VolumetricFogSettings>,
>,
msaa: Res<Msaa>,
meshes: Res<RenderAssets<RenderMesh>>,
) {
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
view_targets.iter()
for (
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.
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 window::*;
use crate::extract_component::ExtractComponentPlugin;
use crate::{
camera::{
CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera,
ManualTextureViews, MipBias, TemporalJitter,
},
extract_resource::{ExtractResource, ExtractResourcePlugin},
prelude::Shader,
primitives::Frustum,
render_asset::RenderAssets,
@ -28,6 +28,7 @@ use bevy_color::LinearRgba;
use bevy_ecs::prelude::*;
use bevy_math::{mat3, vec2, vec3, Mat3, Mat4, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render_macros::ExtractComponent;
use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap;
use std::{
@ -107,10 +108,9 @@ impl Plugin for ViewPlugin {
.register_type::<Visibility>()
.register_type::<VisibleEntities>()
.register_type::<ColorGrading>()
.init_resource::<Msaa>()
// NOTE: windows.is_changed() handles cases where a window was resized
.add_plugins((
ExtractResourcePlugin::<Msaa>::default(),
ExtractComponentPlugin::<Msaa>::default(),
VisibilityPlugin,
VisibilityRangePlugin,
));
@ -139,24 +139,26 @@ impl Plugin for ViewPlugin {
/// 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
/// smoother edges.
/// Defaults to 4 samples.
/// The number of samples to run for Multi-Sample Anti-Aliasing for a given camera. Higher numbers
/// result in smoother edges.
///
/// Defaults to 4 samples. Some advanced rendering features may require that MSAA be disabled.
///
/// 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(
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 {
Off = 1,
Sample2 = 2,
@ -797,7 +799,6 @@ pub fn prepare_view_targets(
mut commands: Commands,
windows: Res<ExtractedWindows>,
images: Res<RenderAssets<GpuImage>>,
msaa: Res<Msaa>,
clear_color_global: Res<ClearColor>,
render_device: Res<RenderDevice>,
mut texture_cache: ResMut<TextureCache>,
@ -806,12 +807,13 @@ pub fn prepare_view_targets(
&ExtractedCamera,
&ExtractedView,
&CameraMainTextureUsages,
&Msaa,
)>,
manual_texture_views: Res<ManualTextureViews>,
) {
let mut 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)
else {
continue;
@ -847,7 +849,7 @@ pub fn prepare_view_targets(
};
let (a, b, sampled, main_texture) = textures
.entry((camera.target.clone(), view.hdr))
.entry((camera.target.clone(), view.hdr, msaa))
.or_insert_with(|| {
let descriptor = TextureDescriptor {
label: None,

View file

@ -30,8 +30,6 @@ use screenshot::{
ScreenshotManager, ScreenshotPlugin, ScreenshotPreparedState, ScreenshotToScreenPipeline,
};
use super::Msaa;
pub struct WindowRenderPlugin;
impl Plugin for WindowRenderPlugin {
@ -250,11 +248,9 @@ pub fn prepare_windows(
mut windows: ResMut<ExtractedWindows>,
mut window_surfaces: ResMut<WindowSurfaces>,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
screenshot_pipeline: Res<ScreenshotToScreenPipeline>,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<ScreenshotToScreenPipeline>>,
mut msaa: ResMut<Msaa>,
#[cfg(target_os = "linux")] render_instance: Res<RenderInstance>,
) {
for window in windows.windows.values_mut() {
@ -263,35 +259,6 @@ pub fn prepare_windows(
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
// 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,

View file

@ -369,7 +369,6 @@ pub fn queue_material2d_meshes<M: Material2d>(
material2d_pipeline: Res<Material2dPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>,
render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>,
mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
@ -379,6 +378,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
Entity,
&ExtractedView,
&VisibleEntities,
&Msaa,
Option<&Tonemapping>,
Option<&DebandDither>,
)>,
@ -389,7 +389,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
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 {
continue;
};

View file

@ -472,26 +472,25 @@ pub fn queue_sprites(
sprite_pipeline: Res<SpritePipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
extracted_sprites: Res<ExtractedSprites>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(
Entity,
&VisibleEntities,
&ExtractedView,
&Msaa,
Option<&Tonemapping>,
Option<&DebandDither>,
)>,
) {
let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples());
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 {
continue;
};
let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples());
let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key;
if !view.hdr {

View file

@ -351,17 +351,16 @@ pub fn queue_colored_mesh2d(
colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<RenderMesh>>,
render_mesh_instances: Res<RenderColoredMesh2dInstances>,
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() {
return;
}
// 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 {
continue;
};

View file

@ -29,7 +29,6 @@ const HIGH_RES_LAYERS: RenderLayers = RenderLayers::layer(1);
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
.insert_resource(Msaa::Off)
.add_systems(Startup, (setup_camera, setup_sprite, setup_mesh))
.add_systems(Update, (rotate, fit_canvas))
.run();
@ -131,6 +130,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
target: RenderTarget::Image(image_handle.clone()),
..default()
},
msaa: Msaa::Off,
..default()
},
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.
// 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.

View file

@ -23,7 +23,6 @@ use bevy::{
fn main() {
App::new()
.insert_resource(Msaa::Off)
.add_plugins((DefaultPlugins, TemporalAntiAliasPlugin))
.add_systems(Startup, setup)
.add_systems(Update, (modify_aa, modify_sharpening, update_ui))
@ -38,13 +37,13 @@ fn modify_aa(
Option<&mut Fxaa>,
Option<&mut SmaaSettings>,
Option<&TemporalAntiAliasSettings>,
&mut Msaa,
),
With<Camera>,
>,
mut msaa: ResMut<Msaa>,
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);
// No AA
@ -176,13 +175,13 @@ fn update_ui(
Option<&SmaaSettings>,
Option<&TemporalAntiAliasSettings>,
&ContrastAdaptiveSharpeningSettings,
&Msaa,
),
With<Camera>,
>,
msaa: Res<Msaa>,
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 ui = &mut ui.sections[0].value;

View file

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

View file

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

View file

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

View file

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

View file

@ -23,14 +23,8 @@ fn main() {
..default()
}))
.add_systems(Startup, (setup_scene, setup_music))
.add_systems(Update, (touch_camera, button_handler, handle_lifetime));
// 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();
.add_systems(Update, (touch_camera, button_handler, handle_lifetime))
.run();
}
fn touch_camera(
@ -109,6 +103,10 @@ fn setup_scene(
// camera
commands.spawn(Camera3dBundle {
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()
});

View file

@ -233,11 +233,10 @@ fn prepare_custom_phase_item_buffers(mut commands: Commands) {
fn queue_custom_phase_item(
pipeline_cache: Res<PipelineCache>,
custom_phase_pipeline: Res<CustomPhasePipeline>,
msaa: Res<Msaa>,
mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
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
.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
// the entity appears in them. (In this example, we have only one view, but
// 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 {
continue;
};

View file

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

View file

@ -34,8 +34,6 @@ fn main() {
))
.add_systems(Startup, setup)
.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();
}
@ -52,6 +50,8 @@ fn setup(
commands.spawn((
Camera3dBundle {
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()
},
// To enable the prepass you need to add the components associated with the ones you need