diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 7d7b7c9afe9ab..0f5da6cbcc25d 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -98,7 +98,7 @@ use bevy_render::{ render_graph::RenderGraph, render_resource::Shader, texture::{GpuImage, Image}, - view::VisibilitySystems, + view::{check_visibility, VisibilitySystems}, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::TransformSystem; @@ -349,6 +349,14 @@ impl Plugin for PbrPlugin { .in_set(SimulationLightSystems::UpdateLightFrusta) .after(TransformSystem::TransformPropagate) .after(SimulationLightSystems::AssignLightsToClusters), + check_visibility:: + .in_set(VisibilitySystems::CheckVisibility) + .after(VisibilitySystems::CalculateBounds) + .after(VisibilitySystems::UpdateOrthographicFrusta) + .after(VisibilitySystems::UpdatePerspectiveFrusta) + .after(VisibilitySystems::UpdateProjectionFrusta) + .after(VisibilitySystems::VisibilityPropagate) + .after(TransformSystem::TransformPropagate), check_light_mesh_visibility .in_set(SimulationLightSystems::CheckLightVisibility) .after(VisibilitySystems::CalculateBounds) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 642c6d0808a35..2b77c5579109c 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -10,10 +10,11 @@ use bevy_render::{ camera::{Camera, CameraProjection}, extract_component::ExtractComponent, extract_resource::ExtractResource, + mesh::Mesh, primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere}, render_resource::BufferBindingType, renderer::RenderDevice, - view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities}, + view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities, WithMesh}, }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::tracing::warn; @@ -98,6 +99,10 @@ impl Default for PointLightShadowMap { } } +/// A convenient alias for `Or<(With, With, +/// With)>`, for use with [`VisibleEntities`]. +pub type WithLight = Or<(With, With, With)>; + /// Controls the resolution of [`DirectionalLight`] shadow maps. #[derive(Resource, Clone, Debug, Reflect)] #[reflect(Resource)] @@ -432,11 +437,11 @@ fn calculate_cascade( texel_size: cascade_texel_size, } } -/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not cast shadows. +/// Add this component to make a [`Mesh`] not cast shadows. #[derive(Component, Reflect, Default)] #[reflect(Component, Default)] pub struct NotShadowCaster; -/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not receive shadows. +/// Add this component to make a [`Mesh`] not receive shadows. /// /// **Note:** If you're using diffuse transmission, setting [`NotShadowReceiver`] will /// cause both “regular” shadows as well as diffusely transmitted shadows to be disabled, @@ -444,7 +449,7 @@ pub struct NotShadowCaster; #[derive(Component, Reflect, Default)] #[reflect(Component, Default)] pub struct NotShadowReceiver; -/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0` +/// Add this component to make a [`Mesh`] using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0` /// receive shadows on its diffuse transmission lobe. (i.e. its “backside”) /// /// Not enabled by default, as it requires carefully setting up [`thickness`](crate::pbr_material::StandardMaterial::thickness) @@ -1859,7 +1864,11 @@ pub fn check_light_mesh_visibility( Option<&Aabb>, Option<&GlobalTransform>, ), - (Without, Without), + ( + Without, + Without, + With>, + ), >, ) { fn shrink_entities(visible_entities: &mut VisibleEntities) { @@ -1947,7 +1956,7 @@ pub fn check_light_mesh_visibility( } view_visibility.set(); - frustum_visible_entities.entities.push(entity); + frustum_visible_entities.get_mut::().push(entity); } } } else { @@ -1959,7 +1968,7 @@ pub fn check_light_mesh_visibility( .expect("Per-view visible entities should have been inserted already"); for frustum_visible_entities in view_visible_entities { - frustum_visible_entities.entities.push(entity); + frustum_visible_entities.get_mut::().push(entity); } } } @@ -2028,13 +2037,13 @@ pub fn check_light_mesh_visibility( { if frustum.intersects_obb(aabb, &model_to_world, true, true) { view_visibility.set(); - visible_entities.entities.push(entity); + visible_entities.push::(entity); } } } else { view_visibility.set(); for visible_entities in cubemap_visible_entities.iter_mut() { - visible_entities.entities.push(entity); + visible_entities.push::(entity); } } } @@ -2089,11 +2098,11 @@ pub fn check_light_mesh_visibility( if frustum.intersects_obb(aabb, &model_to_world, true, true) { view_visibility.set(); - visible_entities.entities.push(entity); + visible_entities.push::(entity); } } else { view_visibility.set(); - visible_entities.entities.push(entity); + visible_entities.push::(entity); } } diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index a616db5b6973c..f80815c88f65f 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -31,7 +31,7 @@ use bevy_render::{ render_resource::*, renderer::RenderDevice, texture::FallbackImage, - view::{ExtractedView, Msaa, VisibleEntities}, + view::{ExtractedView, Msaa, VisibleEntities, WithMesh}, }; use bevy_utils::tracing::error; use std::marker::PhantomData; @@ -645,7 +645,7 @@ pub fn queue_material_meshes( } let rangefinder = view.rangefinder3d(); - for visible_entity in &visible_entities.entities { + for visible_entity in visible_entities.iter::() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 230b7455d83c8..fea98175531ae 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -2,6 +2,7 @@ mod prepass_bindings; use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef}; use bevy_render::render_resource::binding_types::uniform_buffer; +use bevy_render::view::WithMesh; pub use prepass_bindings::*; use bevy_asset::{load_internal_asset, AssetServer}; @@ -774,7 +775,7 @@ pub fn queue_prepass_material_meshes( view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } - for visible_entity in &visible_entities.entities { + for visible_entity in visible_entities.iter::() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 070e1a9d7b99d..7b3464c12472f 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -15,7 +15,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities}, + view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities, WithMesh}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -1647,13 +1647,13 @@ pub fn queue_shadows( .get(*light_entity) .expect("Failed to get spot light visible entities"), }; - // NOTE: Lights with shadow mapping disabled will have no visible entities - // so no meshes will be queued - let mut light_key = MeshPipelineKey::DEPTH_PREPASS; light_key.set(MeshPipelineKey::DEPTH_CLAMP_ORTHO, is_directional_light); - for entity in visible_entities.iter().copied() { + // NOTE: Lights with shadow mapping disabled will have no visible entities + // so no meshes will be queued + + for entity in visible_entities.iter::().copied() { let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity) else { continue; diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index ee8ec2e7d4640..9418f7847a7d1 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,6 +1,9 @@ mod render_layers; +use std::any::TypeId; + use bevy_derive::Deref; +use bevy_ecs::query::QueryFilter; pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; @@ -9,7 +12,7 @@ use bevy_ecs::prelude::*; use bevy_hierarchy::{Children, Parent}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_transform::{components::GlobalTransform, TransformSystem}; -use bevy_utils::Parallel; +use bevy_utils::{Parallel, TypeIdMap}; use crate::{ camera::{ @@ -170,23 +173,67 @@ pub struct NoFrustumCulling; #[reflect(Component, Default)] pub struct VisibleEntities { #[reflect(ignore)] - pub entities: Vec, + pub entities: TypeIdMap>, } impl VisibleEntities { - pub fn iter(&self) -> impl DoubleEndedIterator { - self.entities.iter() + pub fn get(&self) -> &[Entity] + where + QF: 'static, + { + match self.entities.get(&TypeId::of::()) { + Some(entities) => &entities[..], + None => &[], + } + } + + pub fn get_mut(&mut self) -> &mut Vec + where + QF: 'static, + { + self.entities.entry(TypeId::of::()).or_default() + } + + pub fn iter(&self) -> impl DoubleEndedIterator + where + QF: 'static, + { + self.get::().iter() + } + + pub fn len(&self) -> usize + where + QF: 'static, + { + self.get::().len() } - pub fn len(&self) -> usize { - self.entities.len() + pub fn is_empty(&self) -> bool + where + QF: 'static, + { + self.get::().is_empty() } - pub fn is_empty(&self) -> bool { - self.entities.is_empty() + pub fn clear(&mut self) + where + QF: 'static, + { + self.get_mut::().clear(); + } + + pub fn push(&mut self, entity: Entity) + where + QF: 'static, + { + self.get_mut::().push(entity); } } +/// A convenient alias for `With>`, for use with +/// [`VisibleEntities`]. +pub type WithMesh = With>; + #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum VisibilitySystems { /// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems, @@ -238,7 +285,7 @@ impl Plugin for VisibilityPlugin { .after(camera_system::) .after(TransformSystem::TransformPropagate), (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate), - check_visibility + check_visibility:: .in_set(CheckVisibility) .after(CalculateBounds) .after(UpdateOrthographicFrusta) @@ -366,10 +413,15 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { /// System updating the visibility of entities each frame. /// -/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each frame, it updates the -/// [`ViewVisibility`] of all entities, and for each view also compute the [`VisibleEntities`] -/// for that view. -pub fn check_visibility( +/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each +/// frame, it updates the [`ViewVisibility`] of all entities, and for each view +/// also compute the [`VisibleEntities`] for that view. +/// +/// This system needs to be run for each type of renderable entity. If you add a +/// new type of renderable entity, you'll need to add an instantiation of this +/// system to the [`VisibilitySystems::CheckVisibility`] set so that Bevy will +/// detect visibility properly for those entities. +pub fn check_visibility( mut thread_queues: Local>>, mut view_query: Query<( &mut VisibleEntities, @@ -377,16 +429,21 @@ pub fn check_visibility( Option<&RenderLayers>, &Camera, )>, - mut visible_aabb_query: Query<( - Entity, - &InheritedVisibility, - &mut ViewVisibility, - Option<&RenderLayers>, - Option<&Aabb>, - &GlobalTransform, - Has, - )>, -) { + mut visible_aabb_query: Query< + ( + Entity, + &InheritedVisibility, + &mut ViewVisibility, + Option<&RenderLayers>, + Option<&Aabb>, + &GlobalTransform, + Has, + ), + QF, + >, +) where + QF: QueryFilter + 'static, +{ for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query { if !camera.is_active { continue; @@ -394,7 +451,6 @@ pub fn check_visibility( let view_mask = maybe_view_mask.copied().unwrap_or_default(); - visible_entities.entities.clear(); visible_aabb_query.par_iter_mut().for_each(|query_item| { let ( entity, @@ -442,8 +498,8 @@ pub fn check_visibility( }); }); - visible_entities.entities.clear(); - thread_queues.drain_into(&mut visible_entities.entities); + visible_entities.clear::(); + thread_queues.drain_into(visible_entities.get_mut::()); } } diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index 78bed9c8f6a5a..42b715e07a49e 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -32,6 +32,7 @@ pub mod prelude { }; } +use bevy_transform::TransformSystem; pub use bundle::*; pub use dynamic_texture_atlas_builder::*; pub use mesh2d::*; @@ -51,7 +52,7 @@ use bevy_render::{ render_phase::AddRenderCommand, render_resource::{Shader, SpecializedRenderPipelines}, texture::Image, - view::{NoFrustumCulling, VisibilitySystems}, + view::{check_visibility, NoFrustumCulling, VisibilitySystems}, ExtractSchedule, Render, RenderApp, RenderSet, }; @@ -94,6 +95,14 @@ impl Plugin for SpritePlugin { compute_slices_on_sprite_change, ) .in_set(SpriteSystem::ComputeSlices), + check_visibility:: + .in_set(VisibilitySystems::CheckVisibility) + .after(VisibilitySystems::CalculateBounds) + .after(VisibilitySystems::UpdateOrthographicFrusta) + .after(VisibilitySystems::UpdatePerspectiveFrusta) + .after(VisibilitySystems::UpdateProjectionFrusta) + .after(VisibilitySystems::VisibilityPropagate) + .after(TransformSystem::TransformPropagate), ), ); diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 9322b5f4b25a1..682d2296817ff 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -37,7 +37,7 @@ use std::marker::PhantomData; use crate::{ DrawMesh2d, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances, - SetMesh2dBindGroup, SetMesh2dViewBindGroup, + SetMesh2dBindGroup, SetMesh2dViewBindGroup, WithMesh2d, }; /// Materials are used alongside [`Material2dPlugin`] and [`MaterialMesh2dBundle`] @@ -403,7 +403,7 @@ pub fn queue_material2d_meshes( view_key |= Mesh2dPipelineKey::DEBAND_DITHER; } } - for visible_entity in &visible_entities.entities { + for visible_entity in visible_entities.iter::() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 257ad61dfbb7f..21d4fe4fc5710 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -48,6 +48,10 @@ impl From> for Mesh2dHandle { } } +/// A convenient alias for `With`, for use with +/// [`bevy_render::view::VisibleEntities`]. +pub type WithMesh2d = With; + #[derive(Default)] pub struct Mesh2dRenderPlugin; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 202a68dbe20da..8df063e93b5ff 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -2,7 +2,7 @@ use std::ops::Range; use crate::{ texture_atlas::{TextureAtlas, TextureAtlasLayout}, - ComputedTextureSlices, Sprite, SPRITE_SHADER_HANDLE, + ComputedTextureSlices, Sprite, WithMesh2d, SPRITE_SHADER_HANDLE, }; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_color::LinearRgba; @@ -488,7 +488,11 @@ pub fn queue_sprites( let pipeline = pipelines.specialize(&pipeline_cache, &sprite_pipeline, view_key); view_entities.clear(); - view_entities.extend(visible_entities.entities.iter().map(|e| e.index() as usize)); + view_entities.extend( + visible_entities + .iter::() + .map(|e| e.index() as usize), + ); transparent_phase .items diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index e1eacd8d559a9..8db70a2e6c0a5 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -53,7 +53,10 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::prelude::*; use bevy_input::InputSystem; -use bevy_render::RenderApp; +use bevy_render::{ + view::{check_visibility, VisibilitySystems}, + RenderApp, +}; use bevy_transform::TransformSystem; use layout::ui_surface::UiSurface; use stack::ui_stack_system; @@ -98,6 +101,10 @@ struct AmbiguousWithTextSystem; #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] struct AmbiguousWithUpdateText2DLayout; +/// A convenient alias for `With`, for use with +/// [`bevy_render::view::VisibleEntities`]. +pub type WithNode = With; + impl Plugin for UiPlugin { fn build(&self, app: &mut App) { app.init_resource::() @@ -130,6 +137,13 @@ impl Plugin for UiPlugin { app.add_systems( PostUpdate, ( + check_visibility:: + .in_set(VisibilitySystems::CheckVisibility) + .after(VisibilitySystems::CalculateBounds) + .after(VisibilitySystems::UpdateOrthographicFrusta) + .after(VisibilitySystems::UpdatePerspectiveFrusta) + .after(VisibilitySystems::UpdateProjectionFrusta) + .after(VisibilitySystems::VisibilityPropagate), update_target_camera_system.before(UiSystem::Layout), apply_deferred .after(update_target_camera_system) diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 462b0bbfd6c4d..82d65753253ef 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -28,7 +28,7 @@ use bevy::{ sprite::{ extract_mesh2d, DrawMesh2d, Material2dBindGroupId, Mesh2dHandle, Mesh2dPipeline, Mesh2dPipelineKey, Mesh2dTransforms, MeshFlags, RenderMesh2dInstance, - RenderMesh2dInstances, SetMesh2dBindGroup, SetMesh2dViewBindGroup, + RenderMesh2dInstances, SetMesh2dBindGroup, SetMesh2dViewBindGroup, WithMesh2d, }, }; use std::f32::consts::PI; @@ -375,7 +375,7 @@ pub fn queue_colored_mesh2d( | Mesh2dPipelineKey::from_hdr(view.hdr); // Queue all entities visible to that view - for visible_entity in &visible_entities.entities { + for visible_entity in visible_entities.iter::() { if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) { let mesh2d_handle = mesh_instance.mesh_asset_id; let mesh2d_transforms = &mesh_instance.transforms;