diff --git a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs index 1f52beeec8aef..549fcc85f20eb 100644 --- a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs +++ b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs @@ -5,7 +5,7 @@ use bevy_render::{ camera::{Camera, CameraProjection, CameraRenderGraph, OrthographicProjection}, extract_component::ExtractComponent, primitives::Frustum, - view::VisibleEntities, + view::{Msaa, VisibleEntities}, }; use bevy_transform::prelude::{GlobalTransform, Transform}; @@ -36,6 +36,7 @@ pub struct Camera2dBundle { pub global_transform: GlobalTransform, pub camera_2d: Camera2d, pub tonemapping: Tonemapping, + pub msaa: Msaa, } impl Default for Camera2dBundle { @@ -77,6 +78,7 @@ impl Camera2dBundle { camera: Camera::default(), camera_2d: Camera2d::default(), tonemapping: Tonemapping::Disabled, + msaa: Msaa::default(), } } } diff --git a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs index d722f411a21d6..30126c80cb616 100644 --- a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs +++ b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs @@ -6,7 +6,7 @@ use bevy_render::{ extract_component::ExtractComponent, primitives::Frustum, render_resource::LoadOp, - view::VisibleEntities, + view::{Msaa, VisibleEntities}, }; use bevy_transform::prelude::{GlobalTransform, Transform}; use serde::{Deserialize, Serialize}; @@ -68,6 +68,7 @@ pub struct Camera3dBundle { pub global_transform: GlobalTransform, pub camera_3d: Camera3d, pub tonemapping: Tonemapping, + pub msaa: Msaa, } // NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference @@ -85,6 +86,7 @@ impl Default for Camera3dBundle { transform: Default::default(), global_transform: Default::default(), camera_3d: Default::default(), + msaa: Msaa::default(), } } } diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 34a430a345398..2f2dc7bedd52d 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -250,10 +250,9 @@ pub fn extract_core_3d_camera_phases( pub fn prepare_core_3d_depth_textures( mut commands: Commands, mut texture_cache: ResMut, - msaa: Res, render_device: Res, views_3d: Query< - (Entity, &ExtractedCamera), + (Entity, &ExtractedCamera, &Msaa), ( With>, With>, @@ -262,7 +261,7 @@ pub fn prepare_core_3d_depth_textures( >, ) { let mut textures = HashMap::default(); - for (entity, camera) in &views_3d { + for (entity, camera, msaa) in &views_3d { if let Some(physical_target_size) = camera.physical_target_size { let cached_texture = textures .entry(camera.target.clone()) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 622659effd435..3d7c71b41b125 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -333,7 +333,7 @@ pub fn queue_material_meshes( transparent_draw_functions: Res>, material_pipeline: Res>, mut pipelines: ResMut>>, - pipeline_cache: Res, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, render_materials: Res>, @@ -345,6 +345,7 @@ pub fn queue_material_meshes( &mut RenderPhase, &mut RenderPhase, &mut RenderPhase, + &Msaa, )>, ) where M::Data: PartialEq + Eq + Hash + Clone, @@ -356,6 +357,7 @@ pub fn queue_material_meshes( mut opaque_phase, mut alpha_mask_phase, mut transparent_phase, + msaa, ) in &mut views { let draw_opaque_pbr = opaque_draw_functions.read().id::>(); diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 5339043913454..d20515a638b9f 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -108,20 +108,25 @@ fn queue_wireframes( wireframe_config: Res, wireframe_pipeline: Res, mut pipelines: ResMut>, - pipeline_cache: Res, + mut pipeline_cache: ResMut, msaa: Res, mut material_meshes: ParamSet<( Query<(Entity, &Handle, &MeshUniform)>, Query<(Entity, &Handle, &MeshUniform), With>, )>, - mut views: Query<(&ExtractedView, &VisibleEntities, &mut RenderPhase)>, + mut views: Query<( + &ExtractedView, + &VisibleEntities, + &mut RenderPhase, + &Msaa, + )>, ) { let draw_custom = opaque_3d_draw_functions.read().id::(); - let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); - for (view, visible_entities, mut opaque_phase) in &mut views { + for (view, visible_entities, mut opaque_phase, msaa) in &mut views { let rangefinder = view.rangefinder3d(); - let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); + let view_key = + MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_hdr(view.hdr); let add_render_phase = |(entity, mesh_handle, mesh_uniform): (Entity, &Handle, &MeshUniform)| { if let Some(mesh) = render_meshes.get(mesh_handle) { diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index dc6901d8c2e3f..5cd378b7f557f 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -3,7 +3,7 @@ use crate::{ prelude::Image, render_asset::RenderAssets, render_resource::TextureView, - view::{ExtractedView, ExtractedWindows, VisibleEntities}, + view::{ExtractedView, ExtractedWindows, Msaa, VisibleEntities}, Extract, }; use bevy_asset::{AssetEvent, Assets, Handle}; @@ -508,10 +508,11 @@ pub fn extract_cameras( &CameraRenderGraph, &GlobalTransform, &VisibleEntities, + &Msaa, )>, >, ) { - for (entity, camera, camera_render_graph, transform, visible_entities) in query.iter() { + for (entity, camera, camera_render_graph, transform, visible_entities, msaa) in query.iter() { if !camera.is_active { continue; } @@ -544,6 +545,7 @@ pub fn extract_cameras( ), }, visible_entities.clone(), + msaa.clone(), )); } } diff --git a/crates/bevy_render/src/camera/camera_driver_node.rs b/crates/bevy_render/src/camera/camera_driver_node.rs index f57929f30caeb..c64eb5f786e87 100644 --- a/crates/bevy_render/src/camera/camera_driver_node.rs +++ b/crates/bevy_render/src/camera/camera_driver_node.rs @@ -1,3 +1,4 @@ +use crate::prelude::Msaa; use crate::{ camera::{ExtractedCamera, RenderTarget}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue}, @@ -5,11 +6,13 @@ use crate::{ view::ExtractedWindows, }; use bevy_ecs::{entity::Entity, prelude::QueryState, world::World}; +use bevy_utils::hashbrown::hash_map::OccupiedError; +use bevy_utils::HashMap; use bevy_utils::{tracing::warn, HashSet}; use wgpu::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor}; pub struct CameraDriverNode { - cameras: QueryState<(Entity, &'static ExtractedCamera)>, + cameras: QueryState<(Entity, &'static ExtractedCamera, &'static Msaa)>, } impl CameraDriverNode { @@ -33,8 +36,28 @@ impl Node for CameraDriverNode { let mut sorted_cameras = self .cameras .iter_manual(world) - .map(|(e, c)| (e, c.order, c.target.clone())) + .map(|(e, c, _)| (e, c.order, c.target.clone())) .collect::>(); + + // make sure all cameras on the same target have the same `Msaa` + let mut target_msaas = HashMap::new(); + for (_, cam, msaa) in self.cameras.iter_manual(world) { + if let Err(OccupiedError { entry, value }) = + target_msaas.try_insert(cam.target.clone(), msaa) + { + if *entry.get() != value { + // there is another camera with a different Msaa on the same target + warn!( + "There are multiple cameras with different `Msaa`s: ({:?}, {:?}) \ + on the same target: {:?}", + entry.get(), + value, + entry.key() + ); + } + } + } + // sort by order and ensure within an order, RenderTargets of the same type are packed together sorted_cameras.sort_by(|(_, p1, t1), (_, p2, t2)| match p1.cmp(p2) { std::cmp::Ordering::Equal => t1.cmp(t2), @@ -51,7 +74,7 @@ impl Node for CameraDriverNode { } } previous_order_target = Some(new_order_target); - if let Ok((_, camera)) = self.cameras.get_manual(world, entity) { + if let Ok((_, camera, _)) = self.cameras.get_manual(world, entity) { if let RenderTarget::Window(id) = camera.target { camera_windows.insert(id); } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 3bb6479c86e89..b0177c2750274 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -6,7 +6,6 @@ pub use window::*; use crate::{ camera::ExtractedCamera, - extract_resource::{ExtractResource, ExtractResourcePlugin}, prelude::Image, render_asset::RenderAssets, render_phase::ViewRangefinder3d, @@ -37,9 +36,7 @@ impl Plugin for ViewPlugin { .register_type::() .register_type::() .register_type::() - .init_resource::() // NOTE: windows.is_changed() handles cases where a window was resized - .add_plugin(ExtractResourcePlugin::::default()) .add_plugin(VisibilityPlugin); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { @@ -54,18 +51,10 @@ impl Plugin for ViewPlugin { } } -/// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). -/// -/// # Example -/// ``` -/// # use bevy_app::prelude::App; -/// # use bevy_render::prelude::Msaa; -/// App::new() -/// .insert_resource(Msaa { samples: 4 }) -/// .run(); -/// ``` -#[derive(Resource, Clone, ExtractResource, Reflect)] -#[reflect(Resource)] +/// Configuration component for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). +/// Note that having multiple cameras with different Msaa's rendering to the same target will cause an error. +#[derive(Component, Clone, Reflect, PartialEq, Eq, Debug)] +#[reflect(Component)] pub struct Msaa { /// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in /// smoother edges. @@ -278,13 +267,12 @@ fn prepare_view_targets( mut commands: Commands, windows: Res, images: Res>, - msaa: Res, render_device: Res, mut texture_cache: ResMut, - cameras: Query<(Entity, &ExtractedCamera, &ExtractedView)>, + cameras: Query<(Entity, &ExtractedCamera, &ExtractedView, &Msaa)>, ) { let mut textures = HashMap::default(); - for (entity, camera, view) in cameras.iter() { + for (entity, camera, view, msaa) in cameras.iter() { if let Some(target_size) = camera.physical_target_size { if let (Some(out_texture_view), Some(out_texture_format)) = ( camera.target.get_texture_view(&windows, &images), diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index b636e8b2fb782..5b2ad925f601f 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -319,7 +319,7 @@ pub fn queue_material2d_meshes( transparent_draw_functions: Res>, material2d_pipeline: Res>, mut pipelines: ResMut>>, - pipeline_cache: Res, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, render_materials: Res>, @@ -329,6 +329,7 @@ pub fn queue_material2d_meshes( &VisibleEntities, Option<&Tonemapping>, &mut RenderPhase, + &Msaa, )>, ) where M::Data: PartialEq + Eq + Hash + Clone, @@ -337,7 +338,7 @@ pub fn queue_material2d_meshes( return; } - for (view, visible_entities, tonemapping, mut transparent_phase) in &mut views { + for (view, visible_entities, tonemapping, mut transparent_phase, msaa) in &mut views { let draw_transparent_pbr = transparent_draw_functions.read().id::>(); let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples) diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 95fa7cb3f5800..b2a63f786724d 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -454,13 +454,13 @@ pub fn queue_sprites( pipeline_cache: Res, mut image_bind_groups: ResMut, gpu_images: Res>, - msaa: Res, mut extracted_sprites: ResMut, mut views: Query<( &mut RenderPhase, &VisibleEntities, &ExtractedView, Option<&Tonemapping>, + &Msaa, )>, events: Res, ) { @@ -474,8 +474,6 @@ pub fn queue_sprites( }; } - let msaa_key = SpritePipelineKey::from_msaa_samples(msaa.samples); - if let Some(view_binding) = view_uniforms.uniforms.binding() { let sprite_meta = &mut sprite_meta; @@ -516,8 +514,9 @@ pub fn queue_sprites( }); let image_bind_groups = &mut *image_bind_groups; - for (mut transparent_phase, visible_entities, view, tonemapping) in &mut views { - let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key; + for (mut transparent_phase, visible_entities, view, tonemapping, msaa) in &mut views { + let mut view_key = SpritePipelineKey::from_hdr(view.hdr) + | SpritePipelineKey::from_msaa_samples(msaa.samples); if let Some(Tonemapping::Enabled { deband_dither }) = tonemapping { if !view.hdr { view_key |= SpritePipelineKey::TONEMAP_IN_SHADER; diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 560cffa2568e5..c6f7aafa778b1 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -312,7 +312,7 @@ pub fn queue_colored_mesh2d( transparent_draw_functions: Res>, colored_mesh2d_pipeline: Res, mut pipelines: ResMut>, - pipeline_cache: Res, + mut pipeline_cache: ResMut, msaa: Res, render_meshes: Res>, colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With>, @@ -320,13 +320,14 @@ pub fn queue_colored_mesh2d( &VisibleEntities, &mut RenderPhase, &ExtractedView, + &Msaa, )>, ) { if colored_mesh2d.is_empty() { return; } // Iterate each view (a camera is a view) - for (visible_entities, mut transparent_phase, view) in &mut views { + for (visible_entities, mut transparent_phase, view, msaa) in &mut views { let draw_colored_mesh2d = transparent_draw_functions.read().id::(); let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples) diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 9a51145badb35..c2b4dc993c0a7 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -14,7 +14,6 @@ use bevy::{ fn main() { App::new() // Disable MSAA be default - .insert_resource(Msaa { samples: 1 }) .add_plugins(DefaultPlugins) .add_startup_system(setup) .add_system(toggle_fxaa) @@ -103,12 +102,13 @@ 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: Msaa::default(), ..default() }) .insert(Fxaa::default()); } -fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: ResMut) { +fn toggle_fxaa(keys: Res>, mut cameras: Query<(&mut Fxaa, &mut Msaa)>) { let set_no_aa = keys.just_pressed(KeyCode::Key1); let set_msaa = keys.just_pressed(KeyCode::Key2); let set_fxaa = keys.just_pressed(KeyCode::Key3); @@ -118,7 +118,7 @@ fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: let fxaa_ultra = keys.just_pressed(KeyCode::Key9); let fxaa_extreme = keys.just_pressed(KeyCode::Key0); let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra | fxaa_extreme; - for mut fxaa in &mut query { + for (mut fxaa, mut msaa) in &mut cameras { if set_msaa { fxaa.enabled = false; msaa.samples = 4; diff --git a/examples/3d/msaa.rs b/examples/3d/msaa.rs index b59c1ba566c6f..2ccfe605988e2 100644 --- a/examples/3d/msaa.rs +++ b/examples/3d/msaa.rs @@ -10,7 +10,6 @@ use bevy::prelude::*; fn main() { App::new() - .insert_resource(Msaa { samples: 4 }) .add_plugins(DefaultPlugins) .add_startup_system(setup) .add_system(cycle_msaa) @@ -40,12 +39,14 @@ fn setup( // camera commands.spawn(Camera3dBundle { transform: Transform::from_xyz(-3.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + msaa: Msaa { samples: 4 }, ..default() }); } -fn cycle_msaa(input: Res>, mut msaa: ResMut) { +fn cycle_msaa(input: Res>, mut msaa: Query<&mut Msaa>) { if input.just_pressed(KeyCode::M) { + let mut msaa = msaa.single_mut(); if msaa.samples == 4 { info!("Not using MSAA"); msaa.samples = 1; diff --git a/examples/3d/transparency_3d.rs b/examples/3d/transparency_3d.rs index 437c291589f2d..0a3ecb22b0f7c 100644 --- a/examples/3d/transparency_3d.rs +++ b/examples/3d/transparency_3d.rs @@ -6,7 +6,6 @@ use bevy::prelude::*; fn main() { App::new() - .insert_resource(Msaa { samples: 4 }) .add_plugins(DefaultPlugins) .add_startup_system(setup) .add_system(fade_transparency) @@ -101,6 +100,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::default(), ..default() }); } diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 711f0d2293c8c..bf45d49935819 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -102,19 +102,17 @@ struct InstanceData { fn queue_custom( transparent_3d_draw_functions: Res>, custom_pipeline: Res, - msaa: Res, mut pipelines: ResMut>, pipeline_cache: Res, meshes: Res>, material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, - mut views: Query<(&ExtractedView, &mut RenderPhase)>, + mut views: Query<(&ExtractedView, &mut RenderPhase, &Msaa)>, ) { let draw_custom = transparent_3d_draw_functions.read().id::(); - let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); - - for (view, mut transparent_phase) in &mut views { - let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); + for (view, mut transparent_phase, msaa) in &mut views { + let view_key = + MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_hdr(view.hdr); let rangefinder = view.rangefinder3d(); for (entity, mesh_uniform, mesh_handle) in &material_meshes { if let Some(mesh) = meshes.get(mesh_handle) {