diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index ad5bc617a6a2e..88671df5c3575 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -1045,5 +1045,38 @@ mod tests { assert!(ambiguities.contains(entry)); } } + + // Test that anonymous set names work properly + // Related issue https://github.com/bevyengine/bevy/issues/9641 + #[test] + fn anonymous_set_name() { + use super::*; + + #[derive(ScheduleLabel, Hash, PartialEq, Eq, Debug, Clone)] + struct TestSchedule; + + let mut schedule = Schedule::new(TestSchedule); + schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); + + let mut world = World::new(); + schedule.graph_mut().initialize(&mut world); + let _ = schedule + .graph_mut() + .build_schedule(world.components(), &TestSchedule.dyn_clone()); + + let ambiguities: Vec<_> = schedule + .graph() + .conflicts_to_string(schedule.graph().conflicting_systems(), world.components()) + .collect(); + + assert_eq!( + ambiguities[0], + ( + "resmut_system (in set (resmut_system, resmut_system))".to_string(), + "resmut_system (in set (resmut_system, resmut_system))".to_string(), + vec!["bevy_ecs::schedule::tests::system_ambiguity::R"], + ) + ); + } } } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 8b174abdcc7ff..f012cef7ffd3c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1257,10 +1257,15 @@ enum ReportCycles { // methods for reporting errors impl ScheduleGraph { fn get_node_name(&self, id: &NodeId) -> String { + self.get_node_name_inner(id, self.settings.report_sets) + } + + #[inline] + fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String { let mut name = match id { NodeId::System(_) => { let name = self.systems[id.index()].get().unwrap().name().to_string(); - if self.settings.report_sets { + if report_sets { let sets = self.names_of_sets_containing_node(id); if sets.is_empty() { name @@ -1294,7 +1299,8 @@ impl ScheduleGraph { self.hierarchy .graph .edges_directed(*id, Direction::Outgoing) - .map(|(_, member_id, _)| self.get_node_name(&member_id)) + // never get the sets of the members or this will infinite recurse when the report_sets setting is on. + .map(|(_, member_id, _)| self.get_node_name_inner(&member_id, false)) .reduce(|a, b| format!("{a}, {b}")) .unwrap_or_default() ) diff --git a/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs b/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs index f88580e01a5f0..bcabb3d957d73 100644 --- a/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs +++ b/crates/bevy_hierarchy/src/valid_parent_check_plugin.rs @@ -49,7 +49,7 @@ impl Default for ReportHierarchyIssue { /// which parent hasn't a `T` component. /// /// Hierarchy propagations are top-down, and limited only to entities -/// with a specific component (such as `ComputedVisibility` and `GlobalTransform`). +/// with a specific component (such as `InheritedVisibility` and `GlobalTransform`). /// This means that entities with one of those component /// and a parent without the same component is probably a programming error. /// (See B0004 explanation linked in warning message) diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index 0590dba4abdb6..b77ee5b071d5a 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -8,7 +8,7 @@ use bevy_reflect::Reflect; use bevy_render::{ mesh::Mesh, primitives::{CascadesFrusta, CubemapFrusta, Frustum}, - view::{ComputedVisibility, Visibility, VisibleEntities}, + view::{InheritedVisibility, ViewVisibility, Visibility, VisibleEntities}, }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::HashMap; @@ -25,8 +25,10 @@ pub struct MaterialMeshBundle { pub global_transform: GlobalTransform, /// User indication of whether an entity is visible pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } impl Default for MaterialMeshBundle { @@ -37,7 +39,8 @@ impl Default for MaterialMeshBundle { transform: Default::default(), global_transform: Default::default(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), } } } @@ -85,8 +88,10 @@ pub struct PointLightBundle { pub global_transform: GlobalTransform, /// Enables or disables the light pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } /// A component bundle for spot light entities @@ -99,8 +104,10 @@ pub struct SpotLightBundle { pub global_transform: GlobalTransform, /// Enables or disables the light pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } /// A component bundle for [`DirectionalLight`] entities. @@ -115,6 +122,8 @@ pub struct DirectionalLightBundle { pub global_transform: GlobalTransform, /// Enables or disables the light pub visibility: Visibility, + /// Inherited visibility of an entity. + pub visible_in_hieararchy: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 4bc3dc52db79a..228de223caa98 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -208,7 +208,7 @@ impl Plugin for PbrPlugin { .after(CameraUpdateSystem), update_directional_light_frusta .in_set(SimulationLightSystems::UpdateLightFrusta) - // This must run after CheckVisibility because it relies on ComputedVisibility::is_visible() + // This must run after CheckVisibility because it relies on `ViewVisibility` .after(VisibilitySystems::CheckVisibility) .after(TransformSystem::TransformPropagate) .after(SimulationLightSystems::UpdateDirectionalLightCascades) @@ -230,7 +230,7 @@ impl Plugin for PbrPlugin { .after(TransformSystem::TransformPropagate) .after(SimulationLightSystems::UpdateLightFrusta) // NOTE: This MUST be scheduled AFTER the core renderer visibility check - // because that resets entity ComputedVisibility for the first view + // because that resets entity `ViewVisibility` for the first view // which would override any results from this otherwise .after(VisibilitySystems::CheckVisibility), ), diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 08b21db7a3cd6..09b26d8c6e112 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -11,7 +11,7 @@ use bevy_render::{ primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere}, render_resource::BufferBindingType, renderer::RenderDevice, - view::{ComputedVisibility, RenderLayers, VisibleEntities}, + view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities}, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; use bevy_utils::{tracing::warn, HashMap}; @@ -1178,8 +1178,8 @@ pub(crate) fn assign_lights_to_clusters( &mut Clusters, Option<&mut VisiblePointLights>, )>, - point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &ComputedVisibility)>, - spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &ComputedVisibility)>, + point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &ViewVisibility)>, + spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &ViewVisibility)>, mut lights: Local>, mut cluster_aabb_spheres: Local>>, mut max_point_lights_warning_emitted: Local, @@ -1196,7 +1196,7 @@ pub(crate) fn assign_lights_to_clusters( lights.extend( point_lights_query .iter() - .filter(|(.., visibility)| visibility.is_visible()) + .filter(|(.., visibility)| visibility.get()) .map( |(entity, transform, point_light, _visibility)| PointLightAssignmentData { entity, @@ -1210,7 +1210,7 @@ pub(crate) fn assign_lights_to_clusters( lights.extend( spot_lights_query .iter() - .filter(|(.., visibility)| visibility.is_visible()) + .filter(|(.., visibility)| visibility.get()) .map( |(entity, transform, spot_light, _visibility)| PointLightAssignmentData { entity, @@ -1797,7 +1797,7 @@ pub fn update_directional_light_frusta( ( &Cascades, &DirectionalLight, - &ComputedVisibility, + &ViewVisibility, &mut CascadesFrusta, ), ( @@ -1810,7 +1810,7 @@ pub fn update_directional_light_frusta( // The frustum is used for culling meshes to the light for shadow mapping // so if shadow mapping is disabled for this light, then the frustum is // not needed. - if !directional_light.shadows_enabled || !visibility.is_visible() { + if !directional_light.shadows_enabled || !visibility.get() { continue; } @@ -1931,14 +1931,15 @@ pub fn check_light_mesh_visibility( &CascadesFrusta, &mut CascadesVisibleEntities, Option<&RenderLayers>, - &mut ComputedVisibility, + &mut ViewVisibility, ), Without, >, mut visible_entity_query: Query< ( Entity, - &mut ComputedVisibility, + &InheritedVisibility, + &mut ViewVisibility, Option<&RenderLayers>, Option<&Aabb>, Option<&GlobalTransform>, @@ -1963,13 +1964,8 @@ pub fn check_light_mesh_visibility( } // Directional lights - for ( - directional_light, - frusta, - mut visible_entities, - maybe_view_mask, - light_computed_visibility, - ) in &mut directional_lights + for (directional_light, frusta, mut visible_entities, maybe_view_mask, light_view_visibility) in + &mut directional_lights { // Re-use already allocated entries where possible. let mut views_to_remove = Vec::new(); @@ -1995,16 +1991,22 @@ pub fn check_light_mesh_visibility( } // NOTE: If shadow mapping is disabled for the light then it must have no visible entities - if !directional_light.shadows_enabled || !light_computed_visibility.is_visible() { + if !directional_light.shadows_enabled || !light_view_visibility.get() { continue; } let view_mask = maybe_view_mask.copied().unwrap_or_default(); - for (entity, mut computed_visibility, maybe_entity_mask, maybe_aabb, maybe_transform) in - &mut visible_entity_query + for ( + entity, + inherited_visibility, + mut view_visibility, + maybe_entity_mask, + maybe_aabb, + maybe_transform, + ) in &mut visible_entity_query { - if !computed_visibility.is_visible_in_hierarchy() { + if !inherited_visibility.get() { continue; } @@ -2029,12 +2031,12 @@ pub fn check_light_mesh_visibility( continue; } - computed_visibility.set_visible_in_view(); + view_visibility.set(); frustum_visible_entities.entities.push(entity); } } } else { - computed_visibility.set_visible_in_view(); + view_visibility.set(); for view in frusta.frusta.keys() { let view_visible_entities = visible_entities .entities @@ -2081,13 +2083,14 @@ pub fn check_light_mesh_visibility( for ( entity, - mut computed_visibility, + inherited_visibility, + mut view_visibility, maybe_entity_mask, maybe_aabb, maybe_transform, ) in &mut visible_entity_query { - if !computed_visibility.is_visible_in_hierarchy() { + if !inherited_visibility.get() { continue; } @@ -2109,12 +2112,12 @@ pub fn check_light_mesh_visibility( .zip(cubemap_visible_entities.iter_mut()) { if frustum.intersects_obb(aabb, &model_to_world, true, true) { - computed_visibility.set_visible_in_view(); + view_visibility.set(); visible_entities.entities.push(entity); } } } else { - computed_visibility.set_visible_in_view(); + view_visibility.set(); for visible_entities in cubemap_visible_entities.iter_mut() { visible_entities.entities.push(entity); } @@ -2145,13 +2148,14 @@ pub fn check_light_mesh_visibility( for ( entity, - mut computed_visibility, + inherited_visibility, + mut view_visibility, maybe_entity_mask, maybe_aabb, maybe_transform, ) in visible_entity_query.iter_mut() { - if !computed_visibility.is_visible_in_hierarchy() { + if !inherited_visibility.get() { continue; } @@ -2169,11 +2173,11 @@ pub fn check_light_mesh_visibility( } if frustum.intersects_obb(aabb, &model_to_world, true, true) { - computed_visibility.set_visible_in_view(); + view_visibility.set(); visible_entities.entities.push(entity); } } else { - computed_visibility.set_visible_in_view(); + view_visibility.set(); visible_entities.entities.push(entity); } } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index b76eaedda2f65..4e708e454a912 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -22,7 +22,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ComputedVisibility, ExtractedView, VisibleEntities}, + view::{ExtractedView, ViewVisibility, VisibleEntities}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -299,7 +299,7 @@ pub fn extract_lights( &PointLight, &CubemapVisibleEntities, &GlobalTransform, - &ComputedVisibility, + &ViewVisibility, )>, >, spot_lights: Extract< @@ -307,7 +307,7 @@ pub fn extract_lights( &SpotLight, &VisibleEntities, &GlobalTransform, - &ComputedVisibility, + &ViewVisibility, )>, >, directional_lights: Extract< @@ -319,7 +319,7 @@ pub fn extract_lights( &Cascades, &CascadeShadowConfig, &GlobalTransform, - &ComputedVisibility, + &ViewVisibility, ), Without, >, @@ -346,10 +346,10 @@ pub fn extract_lights( let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len); for entity in global_point_lights.iter().copied() { - if let Ok((point_light, cubemap_visible_entities, transform, visibility)) = + if let Ok((point_light, cubemap_visible_entities, transform, view_visibility)) = point_lights.get(entity) { - if !visibility.is_visible() { + if !view_visibility.get() { continue; } // TODO: This is very much not ideal. We should be able to re-use the vector memory. @@ -385,8 +385,10 @@ pub fn extract_lights( let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len); for entity in global_point_lights.iter().copied() { - if let Ok((spot_light, visible_entities, transform, visibility)) = spot_lights.get(entity) { - if !visibility.is_visible() { + if let Ok((spot_light, visible_entities, transform, view_visibility)) = + spot_lights.get(entity) + { + if !view_visibility.get() { continue; } // TODO: This is very much not ideal. We should be able to re-use the vector memory. @@ -433,10 +435,10 @@ pub fn extract_lights( cascades, cascade_config, transform, - visibility, - ) in directional_lights.iter() + view_visibility, + ) in &directional_lights { - if !visibility.is_visible() { + if !view_visibility.get() { continue; } diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 08bac61c89edd..a8127d0ce59ea 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -36,7 +36,7 @@ use bevy_render::{ BevyDefault, DefaultImageSampler, FallbackImageCubemap, FallbackImagesDepth, FallbackImagesMsaa, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, - view::{ComputedVisibility, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms}, + view::{ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, ViewVisibility}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::GlobalTransform; @@ -251,7 +251,7 @@ pub fn extract_meshes( meshes_query: Extract< Query<( Entity, - &ComputedVisibility, + &ViewVisibility, &GlobalTransform, Option<&PreviousGlobalTransform>, &Handle, @@ -262,7 +262,7 @@ pub fn extract_meshes( ) { let mut caster_commands = Vec::with_capacity(*prev_caster_commands_len); let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len); - let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible()); + let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.get()); for (entity, _, transform, previous_transform, handle, not_receiver, not_caster) in visible_meshes @@ -344,7 +344,7 @@ pub fn extract_skinned_meshes( mut commands: Commands, mut previous_len: Local, mut uniform: ResMut, - query: Extract>, + query: Extract>, inverse_bindposes: Extract>>, joint_query: Extract>, ) { @@ -352,8 +352,8 @@ pub fn extract_skinned_meshes( let mut values = Vec::with_capacity(*previous_len); let mut last_start = 0; - for (entity, computed_visibility, skin) in &query { - if !computed_visibility.is_visible() { + for (entity, view_visibility, skin) in &query { + if !view_visibility.get() { continue; } // PERF: This can be expensive, can we move this to prepare? diff --git a/crates/bevy_pbr/src/render/morph.rs b/crates/bevy_pbr/src/render/morph.rs index 43d2e2f585234..753c366726934 100644 --- a/crates/bevy_pbr/src/render/morph.rs +++ b/crates/bevy_pbr/src/render/morph.rs @@ -5,7 +5,7 @@ use bevy_render::{ mesh::morph::{MeshMorphWeights, MAX_MORPH_WEIGHTS}, render_resource::{BufferUsages, BufferVec}, renderer::{RenderDevice, RenderQueue}, - view::ComputedVisibility, + view::ViewVisibility, Extract, }; use bytemuck::Pod; @@ -72,14 +72,14 @@ pub fn extract_morphs( mut commands: Commands, mut previous_len: Local, mut uniform: ResMut, - query: Extract>, + query: Extract>, ) { uniform.buffer.clear(); let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, morph_weights) in &query { - if !computed_visibility.is_visible() { + for (entity, view_visibility, morph_weights) in &query { + if !view_visibility.get() { continue; } let start = uniform.buffer.len(); diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 758c3a7b50a12..df555d392a96b 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -1,7 +1,7 @@ use crate::{ render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType}, renderer::{RenderDevice, RenderQueue}, - view::ComputedVisibility, + view::ViewVisibility, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_app::{App, Plugin}; @@ -222,11 +222,11 @@ fn extract_components( fn extract_visible_components( mut commands: Commands, mut previous_len: Local, - query: Extract>, + query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, query_item) in &query { - if computed_visibility.is_visible() { + for (entity, view_visibility, query_item) in &query { + if view_visibility.get() { if let Some(component) = C::extract_component(query_item) { values.push((entity, component)); } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 1c544320b927b..ee6c96e6fe4f9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -33,7 +33,7 @@ pub mod prelude { render_resource::Shader, spatial_bundle::SpatialBundle, texture::{Image, ImagePlugin}, - view::{ComputedVisibility, Msaa, Visibility, VisibilityBundle}, + view::{InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibilityBundle}, ExtractSchedule, }; } @@ -353,7 +353,7 @@ impl Plugin for RenderPlugin { } app.add_plugins(( - ValidParentCheckPlugin::::default(), + ValidParentCheckPlugin::::default(), WindowRenderPlugin, CameraPlugin, ViewPlugin, diff --git a/crates/bevy_render/src/spatial_bundle.rs b/crates/bevy_render/src/spatial_bundle.rs index b2d50da6097f6..5287c3880460a 100644 --- a/crates/bevy_render/src/spatial_bundle.rs +++ b/crates/bevy_render/src/spatial_bundle.rs @@ -1,14 +1,14 @@ use bevy_ecs::prelude::Bundle; use bevy_transform::prelude::{GlobalTransform, Transform}; -use crate::view::{ComputedVisibility, Visibility}; +use crate::view::{InheritedVisibility, ViewVisibility, Visibility}; /// A [`Bundle`] with the following [`Component`](bevy_ecs::component::Component)s: -/// * [`Visibility`] and [`ComputedVisibility`], which describe the visibility of an entity +/// * [`Visibility`], and [`InheritedVisibility`], which describe the visibility of an entity /// * [`Transform`] and [`GlobalTransform`], which describe the position of an entity /// /// * To show or hide an entity, you should set its [`Visibility`]. -/// * To get the computed visibility of an entity, you should get its [`ComputedVisibility`]. +/// * To get the computed visibility of an entity, you should get its [`InheritedVisibility`] or [`ViewVisibility`] components. /// * To place or move an entity, you should set its [`Transform`]. /// * To get the global transform of an entity, you should get its [`GlobalTransform`]. /// * For hierarchies to work correctly, you must have all four components. @@ -17,8 +17,10 @@ use crate::view::{ComputedVisibility, Visibility}; pub struct SpatialBundle { /// The visibility of the entity. pub visibility: Visibility, - /// The computed visibility of the entity. - pub computed: ComputedVisibility, + /// The inherited visibility of the entity. + pub inherited_visibility: InheritedVisibility, + /// The view visibility of the entity. + pub view_visibility: ViewVisibility, /// The transform of the entity. pub transform: Transform, /// The global transform of the entity. @@ -40,7 +42,8 @@ impl SpatialBundle { /// A visible [`SpatialBundle`], with no translation, rotation, and a scale of 1 on all axes. pub const INHERITED_IDENTITY: Self = SpatialBundle { visibility: Visibility::Inherited, - computed: ComputedVisibility::HIDDEN, + inherited_visibility: InheritedVisibility::HIDDEN, + view_visibility: ViewVisibility::HIDDEN, transform: Transform::IDENTITY, global_transform: GlobalTransform::IDENTITY, }; diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 49f85e8806902..012a62f76d80c 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -39,8 +39,8 @@ impl Plugin for ViewPlugin { fn build(&self, app: &mut App) { load_internal_asset!(app, VIEW_TYPE_HANDLE, "view.wgsl", Shader::from_wgsl); - app.register_type::() - .register_type::() + app.register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 538b6a03ee7e4..719bebe585ec3 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,5 +1,6 @@ mod render_layers; +use bevy_derive::Deref; pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; @@ -26,7 +27,7 @@ use crate::{ /// are set to [`Inherited`](Self::Inherited) will also be hidden. /// /// This is done by the `visibility_propagate_system` which uses the entity hierarchy and -/// `Visibility` to set the values of each entity's [`ComputedVisibility`] component. +/// `Visibility` to set the values of each entity's [`InheritedVisibility`] component. #[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)] #[reflect(Component, Default)] pub enum Visibility { @@ -60,97 +61,88 @@ impl std::cmp::PartialEq<&Visibility> for Visibility { } } -bitflags::bitflags! { - #[derive(Clone, Debug, Eq, PartialEq)] - pub(super) struct ComputedVisibilityFlags: u8 { - const VISIBLE_IN_VIEW = 1 << 0; - const VISIBLE_IN_HIERARCHY = 1 << 1; - } -} -bevy_reflect::impl_reflect_value!((in bevy_render::view) ComputedVisibilityFlags); - -/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering -#[derive(Component, Clone, Reflect, Debug, Eq, PartialEq)] +/// Whether or not an entity is visible in the hierarchy. +/// This will not be accurate until [`VisibilityPropagate`] runs in the [`PostUpdate`] schedule. +/// +/// If this is false, then [`ViewVisibility`] should also be false. +/// +/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate +#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)] #[reflect(Component, Default)] -pub struct ComputedVisibility { - flags: ComputedVisibilityFlags, -} +pub struct InheritedVisibility(bool); -impl Default for ComputedVisibility { - fn default() -> Self { - Self::HIDDEN - } -} - -impl ComputedVisibility { - /// A [`ComputedVisibility`], set as invisible. - pub const HIDDEN: Self = ComputedVisibility { - flags: ComputedVisibilityFlags::empty(), - }; +impl InheritedVisibility { + /// An entity that is invisible in the hierarchy. + pub const HIDDEN: Self = Self(false); + /// An entity that is visible in the hierarchy. + pub const VISIBLE: Self = Self(true); - /// Whether this entity is visible to something this frame. This is true if and only if [`Self::is_visible_in_hierarchy`] and [`Self::is_visible_in_view`] - /// are true. This is the canonical method to call to determine if an entity should be drawn. - /// This value is updated in [`PostUpdate`] by the [`VisibilitySystems::CheckVisibility`] system set. - /// Reading it during [`Update`](bevy_app::Update) will yield the value from the previous frame. + /// Returns `true` if the entity is visible in the hierarchy. + /// Otherwise, returns `false`. #[inline] - pub fn is_visible(&self) -> bool { - self.flags.bits() == ComputedVisibilityFlags::all().bits() + pub fn get(self) -> bool { + self.0 } +} - /// Whether this entity is visible in the entity hierarchy, which is determined by the [`Visibility`] component. - /// This takes into account "visibility inheritance". If any of this entity's ancestors (see [`Parent`]) are hidden, this entity - /// will be hidden as well. This value is updated in the [`VisibilitySystems::VisibilityPropagate`], which lives in the [`PostUpdate`] schedule. - #[inline] - pub fn is_visible_in_hierarchy(&self) -> bool { - self.flags - .contains(ComputedVisibilityFlags::VISIBLE_IN_HIERARCHY) - } +/// Algorithmically-computed indication or whether an entity is visible and should be extracted for rendering. +/// +/// Each frame, this will be reset to `false` during [`VisibilityPropagate`] systems in [`PostUpdate`]. +/// Later in the frame, systems in [`CheckVisibility`] will mark any visible entities using [`ViewVisibility::set`]. +/// Because of this, values of this type will be marked as changed every frame, even when they do not change. +/// +/// If you wish to add custom visibility system that sets this value, make sure you add it to the [`CheckVisibility`] set. +/// +/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate +/// [`CheckVisibility`]: VisibilitySystems::CheckVisibility +#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)] +#[reflect(Component, Default)] +pub struct ViewVisibility(bool); - /// Whether this entity is visible in _any_ view (Cameras, Lights, etc). Each entity type (and view type) should choose how to set this - /// value. For cameras and drawn entities, this will take into account [`RenderLayers`]. - /// - /// This value is reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] during [`PostUpdate`]. - /// Each entity type then chooses how to set this field in the [`VisibilitySystems::CheckVisibility`] system set, in [`PostUpdate`]. - /// Meshes might use frustum culling to decide if they are visible in a view. - /// Other entities might just set this to `true` every frame. - #[inline] - pub fn is_visible_in_view(&self) -> bool { - self.flags - .contains(ComputedVisibilityFlags::VISIBLE_IN_VIEW) - } +impl ViewVisibility { + /// An entity that cannot be seen from any views. + pub const HIDDEN: Self = Self(false); - /// Sets `is_visible_in_view` to `true`. This is not reversible for a given frame, as it encodes whether or not this is visible in - /// _any_ view. This will be automatically reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] and then set - /// to the proper value in [`VisibilitySystems::CheckVisibility`]. This should _only_ be set in systems with the [`VisibilitySystems::CheckVisibility`] - /// label. Don't call this unless you are defining a custom visibility system. For normal user-defined entity visibility, see [`Visibility`]. + /// Returns `true` if the entity is visible in any view. + /// Otherwise, returns `false`. #[inline] - pub fn set_visible_in_view(&mut self) { - self.flags.insert(ComputedVisibilityFlags::VISIBLE_IN_VIEW); + pub fn get(self) -> bool { + self.0 } + /// Sets the visibility to `true`. This should not be considered reversible for a given frame, + /// as this component tracks whether or not the entity visible in _any_ view. + /// + /// This will be automatically reset to `false` every frame in [`VisibilityPropagate`] and then set + /// to the proper value in [`CheckVisibility`]. + /// + /// You should only manaully set this if you are defining a custom visibility system, + /// in which case the system should be placed in the [`CheckVisibility`] set. + /// For normal user-defined entity visibility, see [`Visibility`]. + /// + /// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate + /// [`CheckVisibility`]: VisibilitySystems::CheckVisibility #[inline] - fn reset(&mut self, visible_in_hierarchy: bool) { - self.flags = if visible_in_hierarchy { - ComputedVisibilityFlags::VISIBLE_IN_HIERARCHY - } else { - ComputedVisibilityFlags::empty() - }; + pub fn set(&mut self) { + self.0 = true; } } -/// A [`Bundle`] of the [`Visibility`] and [`ComputedVisibility`] +/// A [`Bundle`] of the [`Visibility`], [`InheritedVisibility`], and [`ViewVisibility`] /// [`Component`](bevy_ecs::component::Component)s, which describe the visibility of an entity. /// /// * To show or hide an entity, you should set its [`Visibility`]. -/// * To get the computed visibility of an entity, you should get its [`ComputedVisibility`]. -/// * For visibility hierarchies to work correctly, you must have both a [`Visibility`] and a [`ComputedVisibility`]. +/// * To get the inherited visibility of an entity, you should get its [`InheritedVisibility`]. +/// * For visibility hierarchies to work correctly, you must have both all of [`Visibility`], [`InheritedVisibility`], and [`ViewVisibility`]. /// * You may use the [`VisibilityBundle`] to guarantee this. #[derive(Bundle, Debug, Default)] pub struct VisibilityBundle { /// The visibility of the entity. pub visibility: Visibility, - /// The computed visibility of the entity. - pub computed: ComputedVisibility, + // The inherited visibility of the entity. + pub inherited_visibility: InheritedVisibility, + // The computed visibility of the entity. + pub view_visibility: ViewVisibility, } /// Use this component to opt-out of built-in frustum culling for entities, see @@ -207,10 +199,10 @@ pub enum VisibilitySystems { UpdatePerspectiveFrusta, /// Label for the [`update_frusta`] system. UpdateProjectionFrusta, - /// Label for the system propagating the [`ComputedVisibility`] in a + /// Label for the system propagating the [`InheritedVisibility`] in a /// [`hierarchy`](bevy_hierarchy). VisibilityPropagate, - /// Label for the [`check_visibility`] system updating [`ComputedVisibility`] + /// Label for the [`check_visibility`] system updating [`ViewVisibility`] /// of each entity and the [`VisibleEntities`] of each view. CheckVisibility, } @@ -250,7 +242,8 @@ impl Plugin for VisibilityPlugin { .in_set(UpdateProjectionFrusta) .after(camera_system::) .after(TransformSystem::TransformPropagate), - visibility_propagate_system.in_set(VisibilityPropagate), + (visibility_propagate_system, reset_view_visibility) + .in_set(VisibilityPropagate), check_visibility .in_set(CheckVisibility) .after(CalculateBoundsFlush) @@ -306,83 +299,108 @@ pub fn update_frusta( } fn visibility_propagate_system( - mut root_query: Query< - ( - Option<&Children>, - &Visibility, - &mut ComputedVisibility, - Entity, - ), - Without, + changed: Query< + (Entity, &Visibility, Option<&Parent>, Option<&Children>), + (With, Changed), >, - mut visibility_query: Query<(&Visibility, &mut ComputedVisibility, &Parent)>, - children_query: Query<&Children, (With, With, With)>, + mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>, + children_query: Query<&Children, (With, With)>, ) { - for (children, visibility, mut computed_visibility, entity) in root_query.iter_mut() { - // reset "view" visibility here ... if this entity should be drawn a future system should set this to true - computed_visibility - .reset(visibility == Visibility::Inherited || visibility == Visibility::Visible); - if let Some(children) = children { - for child in children.iter() { - let _ = propagate_recursive( - computed_visibility.is_visible_in_hierarchy(), - &mut visibility_query, - &children_query, - *child, - entity, - ); + for (entity, visibility, parent, children) in &changed { + let is_visible = match visibility { + Visibility::Visible => true, + Visibility::Hidden => false, + Visibility::Inherited => match parent { + None => true, + Some(parent) => visibility_query.get(parent.get()).unwrap().1.get(), + }, + }; + let (_, mut inherited_visiblity) = visibility_query + .get_mut(entity) + .expect("With ensures this query will return a value"); + + // Only update the visibility if it has changed. + // This will also prevent the visibility from propagating multiple times in the same frame + // if this entity's visiblity has been updated recursively by its parent. + if inherited_visiblity.get() != is_visible { + inherited_visiblity.0 = is_visible; + + // Recursively update the visibility of each child. + for &child in children.into_iter().flatten() { + let _ = + propagate_recursive(is_visible, child, &mut visibility_query, &children_query); } } } } fn propagate_recursive( - parent_visible: bool, - visibility_query: &mut Query<(&Visibility, &mut ComputedVisibility, &Parent)>, - children_query: &Query<&Children, (With, With, With)>, + parent_is_visible: bool, entity: Entity, - expected_parent: Entity, + visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>, + children_query: &Query<&Children, (With, With)>, // BLOCKED: https://github.com/rust-lang/rust/issues/31436 // We use a result here to use the `?` operator. Ideally we'd use a try block instead ) -> Result<(), ()> { - let is_visible = { - let (visibility, mut computed_visibility, child_parent) = - visibility_query.get_mut(entity).map_err(drop)?; - assert_eq!( - child_parent.get(), expected_parent, - "Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle" - ); - let visible_in_hierarchy = (parent_visible && visibility == Visibility::Inherited) - || visibility == Visibility::Visible; - // reset "view" visibility here ... if this entity should be drawn a future system should set this to true - computed_visibility.reset(visible_in_hierarchy); - visible_in_hierarchy + // Get the visibility components for the current entity. + // If the entity does not have the requuired components, just return early. + let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?; + + let is_visible = match visibility { + Visibility::Visible => true, + Visibility::Hidden => false, + Visibility::Inherited => parent_is_visible, }; - for child in children_query.get(entity).map_err(drop)?.iter() { - let _ = propagate_recursive(is_visible, visibility_query, children_query, *child, entity); + // Only update the visibility if it has changed. + if inherited_visibility.get() != is_visible { + inherited_visibility.0 = is_visible; + + // Recursively update the visibility of each child. + for &child in children_query.get(entity).ok().into_iter().flatten() { + let _ = propagate_recursive(is_visible, child, visibility_query, children_query); + } } + Ok(()) } -/// Updates the visibility of entities each frame. +/// Resets the view visibility of every entity. +/// Entities that are visible will be marked as such later this frame +/// by a [`VisibilitySystems::CheckVisibility`] system. +fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { + for mut view_visibility in &mut query { + // NOTE: We do not use `set_if_neq` here, as we don't care about + // change detection for view visibility, and adding a branch to every + // loop iteration would pessimize performance. + *view_visibility = ViewVisibility::HIDDEN; + } +} + +/// System updating the visibility of entities each frame. /// -/// This system is part of the [`VisibilitySystems::CheckVisibility`] set. Each frame, it updates the -/// [`ComputedVisibility`] of all entities, and for each view also compute the [`VisibleEntities`] +/// 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( mut thread_queues: Local>>>, mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With>, mut visible_aabb_query: Query<( Entity, - &mut ComputedVisibility, + &InheritedVisibility, + &mut ViewVisibility, Option<&RenderLayers>, &Aabb, &GlobalTransform, Option<&NoFrustumCulling>, )>, mut visible_no_aabb_query: Query< - (Entity, &mut ComputedVisibility, Option<&RenderLayers>), + ( + Entity, + &InheritedVisibility, + &mut ViewVisibility, + Option<&RenderLayers>, + ), Without, >, ) { @@ -393,15 +411,16 @@ pub fn check_visibility( visible_aabb_query.par_iter_mut().for_each( |( entity, - mut computed_visibility, + inherited_visibility, + mut view_visibility, maybe_entity_mask, model_aabb, transform, maybe_no_frustum_culling, )| { - // skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false - // in visibility_propagate_system - if !computed_visibility.is_visible_in_hierarchy() { + // Skip computing visibility for entities that are configured to be hidden. + // ViewVisibility has already been reset in `reset_view_visibility`. + if !inherited_visibility.get() { return; } @@ -427,7 +446,7 @@ pub fn check_visibility( } } - computed_visibility.set_visible_in_view(); + view_visibility.set(); let cell = thread_queues.get_or_default(); let mut queue = cell.take(); queue.push(entity); @@ -436,10 +455,10 @@ pub fn check_visibility( ); visible_no_aabb_query.par_iter_mut().for_each( - |(entity, mut computed_visibility, maybe_entity_mask)| { - // skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false - // in visibility_propagate_system - if !computed_visibility.is_visible_in_hierarchy() { + |(entity, inherited_visibility, mut view_visibility, maybe_entity_mask)| { + // Skip computing visibility for entities that are configured to be hidden. + // ViewVisiblity has already been reset in `reset_view_visibility`. + if !inherited_visibility.get() { return; } @@ -448,7 +467,7 @@ pub fn check_visibility( return; } - computed_visibility.set_visible_in_view(); + view_visibility.set(); let cell = thread_queues.get_or_default(); let mut queue = cell.take(); queue.push(entity); @@ -478,24 +497,21 @@ mod test { let root1 = app .world - .spawn((Visibility::Hidden, ComputedVisibility::default())) - .id(); - let root1_child1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) .id(); + let root1_child1 = app.world.spawn(VisibilityBundle::default()).id(); let root1_child2 = app .world - .spawn((Visibility::Hidden, ComputedVisibility::default())) - .id(); - let root1_child1_grandchild1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) - .id(); - let root1_child2_grandchild1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) .id(); + let root1_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); + let root1_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); app.world .entity_mut(root1) @@ -507,26 +523,17 @@ mod test { .entity_mut(root1_child2) .push_children(&[root1_child2_grandchild1]); - let root2 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) - .id(); - let root2_child1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) - .id(); + let root2 = app.world.spawn(VisibilityBundle::default()).id(); + let root2_child1 = app.world.spawn(VisibilityBundle::default()).id(); let root2_child2 = app .world - .spawn((Visibility::Hidden, ComputedVisibility::default())) - .id(); - let root2_child1_grandchild1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) - .id(); - let root2_child2_grandchild1 = app - .world - .spawn((Visibility::default(), ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) .id(); + let root2_child1_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); + let root2_child2_grandchild1 = app.world.spawn(VisibilityBundle::default()).id(); app.world .entity_mut(root2) @@ -543,9 +550,9 @@ mod test { let is_visible = |e: Entity| { app.world .entity(e) - .get::() + .get::() .unwrap() - .is_visible_in_hierarchy() + .get() }; assert!( !is_visible(root1), @@ -597,32 +604,53 @@ mod test { let root1 = app .world - .spawn((Visibility::Visible, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Visible, + ..Default::default() + }) .id(); let root1_child1 = app .world - .spawn((Visibility::Inherited, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Inherited, + ..Default::default() + }) .id(); let root1_child2 = app .world - .spawn((Visibility::Hidden, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) .id(); let root1_child1_grandchild1 = app .world - .spawn((Visibility::Visible, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Visible, + ..Default::default() + }) .id(); let root1_child2_grandchild1 = app .world - .spawn((Visibility::Visible, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Visible, + ..Default::default() + }) .id(); let root2 = app .world - .spawn((Visibility::Inherited, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Inherited, + ..Default::default() + }) .id(); let root3 = app .world - .spawn((Visibility::Hidden, ComputedVisibility::default())) + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) .id(); app.world @@ -640,9 +668,9 @@ mod test { let is_visible = |e: Entity| { app.world .entity(e) - .get::() + .get::() .unwrap() - .is_visible_in_hierarchy() + .get() }; assert!( is_visible(root1), @@ -668,6 +696,87 @@ mod test { assert!(!is_visible(root3), "a hidden root is hidden"); } + #[test] + fn visibility_progation_change_detection() { + let mut world = World::new(); + let mut schedule = Schedule::default(); + schedule.add_systems(visibility_propagate_system); + + // Set up an entity hierarchy. + + let id1 = world.spawn(VisibilityBundle::default()).id(); + + let id2 = world.spawn(VisibilityBundle::default()).id(); + world.entity_mut(id1).push_children(&[id2]); + + let id3 = world + .spawn(VisibilityBundle { + visibility: Visibility::Hidden, + ..Default::default() + }) + .id(); + world.entity_mut(id2).push_children(&[id3]); + + let id4 = world.spawn(VisibilityBundle::default()).id(); + world.entity_mut(id3).push_children(&[id4]); + + // Test the hierarchy. + + // Make sure the hierarchy is up-to-date. + schedule.run(&mut world); + world.clear_trackers(); + + let mut q = world.query::>(); + + assert!(!q.get(&world, id1).unwrap().is_changed()); + assert!(!q.get(&world, id2).unwrap().is_changed()); + assert!(!q.get(&world, id3).unwrap().is_changed()); + assert!(!q.get(&world, id4).unwrap().is_changed()); + + world.clear_trackers(); + world.entity_mut(id1).insert(Visibility::Hidden); + schedule.run(&mut world); + + assert!(q.get(&world, id1).unwrap().is_changed()); + assert!(q.get(&world, id2).unwrap().is_changed()); + assert!(!q.get(&world, id3).unwrap().is_changed()); + assert!(!q.get(&world, id4).unwrap().is_changed()); + + world.clear_trackers(); + schedule.run(&mut world); + + assert!(!q.get(&world, id1).unwrap().is_changed()); + assert!(!q.get(&world, id2).unwrap().is_changed()); + assert!(!q.get(&world, id3).unwrap().is_changed()); + assert!(!q.get(&world, id4).unwrap().is_changed()); + + world.clear_trackers(); + world.entity_mut(id3).insert(Visibility::Inherited); + schedule.run(&mut world); + + assert!(!q.get(&world, id1).unwrap().is_changed()); + assert!(!q.get(&world, id2).unwrap().is_changed()); + assert!(!q.get(&world, id3).unwrap().is_changed()); + assert!(!q.get(&world, id4).unwrap().is_changed()); + + world.clear_trackers(); + world.entity_mut(id2).insert(Visibility::Visible); + schedule.run(&mut world); + + assert!(!q.get(&world, id1).unwrap().is_changed()); + assert!(q.get(&world, id2).unwrap().is_changed()); + assert!(q.get(&world, id3).unwrap().is_changed()); + assert!(q.get(&world, id4).unwrap().is_changed()); + + world.clear_trackers(); + schedule.run(&mut world); + + assert!(!q.get(&world, id1).unwrap().is_changed()); + assert!(!q.get(&world, id2).unwrap().is_changed()); + assert!(!q.get(&world, id3).unwrap().is_changed()); + assert!(!q.get(&world, id4).unwrap().is_changed()); + } + #[test] fn ensure_visibility_enum_size() { use std::mem; diff --git a/crates/bevy_scene/src/bundle.rs b/crates/bevy_scene/src/bundle.rs index 1e66e5729325a..48112de710586 100644 --- a/crates/bevy_scene/src/bundle.rs +++ b/crates/bevy_scene/src/bundle.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ system::{Commands, Query}, }; #[cfg(feature = "bevy_render")] -use bevy_render::prelude::{ComputedVisibility, Visibility}; +use bevy_render::prelude::{InheritedVisibility, ViewVisibility, Visibility}; use bevy_transform::components::{GlobalTransform, Transform}; use crate::{DynamicScene, InstanceId, Scene, SceneSpawner}; @@ -32,7 +32,9 @@ pub struct SceneBundle { #[cfg(feature = "bevy_render")] pub visibility: Visibility, #[cfg(feature = "bevy_render")] - pub computed_visibility: ComputedVisibility, + pub inherited_visibility: InheritedVisibility, + #[cfg(feature = "bevy_render")] + pub view_visibility: ViewVisibility, } /// A component bundle for a [`DynamicScene`] root. @@ -49,7 +51,9 @@ pub struct DynamicSceneBundle { #[cfg(feature = "bevy_render")] pub visibility: Visibility, #[cfg(feature = "bevy_render")] - pub computed_visibility: ComputedVisibility, + pub inherited_visibility: InheritedVisibility, + #[cfg(feature = "bevy_render")] + pub view_visibility: ViewVisibility, } /// System that will spawn scenes from [`SceneBundle`]. diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index 8fbb3abaa7267..7013993482347 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -6,7 +6,7 @@ use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; use bevy_render::{ texture::Image, - view::{ComputedVisibility, Visibility}, + view::{InheritedVisibility, ViewVisibility, Visibility}, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -18,8 +18,10 @@ pub struct SpriteBundle { pub texture: Handle, /// User indication of whether an entity is visible pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } /// A Bundle of components for drawing a single sprite from a sprite sheet (also referred @@ -35,6 +37,7 @@ pub struct SpriteSheetBundle { pub global_transform: GlobalTransform, /// User indication of whether an entity is visible pub visibility: Visibility, + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 628a118f3a96b..abea539b9acd3 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -30,7 +30,7 @@ use bevy_render::{ }, renderer::RenderDevice, texture::FallbackImage, - view::{ComputedVisibility, ExtractedView, Msaa, Visibility, VisibleEntities}, + view::{ExtractedView, InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibleEntities}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -585,8 +585,10 @@ pub struct MaterialMesh2dBundle { pub global_transform: GlobalTransform, /// User indication of whether an entity is visible pub visibility: Visibility, - /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + // Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, + // Indication of whether an entity is visible in any view. + pub view_visibility: ViewVisibility, } impl Default for MaterialMesh2dBundle { @@ -597,7 +599,8 @@ impl Default for MaterialMesh2dBundle { transform: Default::default(), global_transform: Default::default(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), } } } diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 6453861fda82c..09b69b296a664 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -20,7 +20,7 @@ use bevy_render::{ BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, view::{ - ComputedVisibility, ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, + ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, ViewVisibility, }, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; @@ -132,11 +132,11 @@ bitflags::bitflags! { pub fn extract_mesh2d( mut commands: Commands, mut previous_len: Local, - query: Extract>, + query: Extract>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility, transform, handle) in &query { - if !computed_visibility.is_visible() { + for (entity, view_visibility, transform, handle) in &query { + if !view_visibility.get() { continue; } let transform = transform.compute_matrix(); diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 1253baf9abdb3..ccbab38d42bfd 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -28,8 +28,8 @@ use bevy_render::{ BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, }, view::{ - ComputedVisibility, ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, - ViewUniforms, VisibleEntities, + ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, + ViewVisibility, VisibleEntities, }, Extract, }; @@ -338,7 +338,7 @@ pub fn extract_sprites( sprite_query: Extract< Query<( Entity, - &ComputedVisibility, + &ViewVisibility, &Sprite, &GlobalTransform, &Handle, @@ -347,15 +347,15 @@ pub fn extract_sprites( atlas_query: Extract< Query<( Entity, - &ComputedVisibility, + &ViewVisibility, &TextureAtlasSprite, &GlobalTransform, &Handle, )>, >, ) { - for (entity, visibility, sprite, transform, handle) in sprite_query.iter() { - if !visibility.is_visible() { + for (entity, view_visibility, sprite, transform, handle) in sprite_query.iter() { + if !view_visibility.get() { continue; } // PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive @@ -374,8 +374,10 @@ pub fn extract_sprites( }, ); } - for (entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() { - if !visibility.is_visible() { + for (entity, view_visibility, atlas_sprite, transform, texture_atlas_handle) in + atlas_query.iter() + { + if !view_visibility.get() { continue; } if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) { diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index eba76ab59961f..1f26e47d21367 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -18,7 +18,7 @@ use bevy_reflect::Reflect; use bevy_render::{ prelude::Color, texture::Image, - view::{ComputedVisibility, Visibility}, + view::{InheritedVisibility, ViewVisibility, Visibility}, Extract, }; use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas}; @@ -69,8 +69,10 @@ pub struct Text2dBundle { pub global_transform: GlobalTransform, /// The visibility properties of the text. pub visibility: Visibility, - /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering. - pub computed_visibility: ComputedVisibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, + /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering + pub view_visibility: ViewVisibility, /// Contains the size of the text and its glyph's position and scale data. Generated via [`TextPipeline::queue_text`] pub text_layout_info: TextLayoutInfo, } @@ -82,7 +84,7 @@ pub fn extract_text2d_sprite( windows: Extract>>, text2d_query: Extract< Query<( - &ComputedVisibility, + &ViewVisibility, &Text, &TextLayoutInfo, &Anchor, @@ -97,10 +99,8 @@ pub fn extract_text2d_sprite( .unwrap_or(1.0); let scaling = GlobalTransform::from_scale(Vec3::splat(scale_factor.recip())); - for (computed_visibility, text, text_layout_info, anchor, global_transform) in - text2d_query.iter() - { - if !computed_visibility.is_visible() { + for (view_visibility, text, text_layout_info, anchor, global_transform) in text2d_query.iter() { + if !view_visibility.get() { continue; } diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index af6dc0f278cc4..c5211cd14850e 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -11,7 +11,7 @@ use bevy_ecs::{ use bevy_input::{mouse::MouseButton, touch::Touches, Input}; use bevy_math::Vec2; use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; -use bevy_render::{camera::NormalizedRenderTarget, prelude::Camera, view::ComputedVisibility}; +use bevy_render::{camera::NormalizedRenderTarget, prelude::Camera, view::ViewVisibility}; use bevy_transform::components::GlobalTransform; use bevy_window::{PrimaryWindow, Window}; @@ -24,9 +24,9 @@ use smallvec::SmallVec; /// /// Updated in [`ui_focus_system`]. /// -/// If a UI node has both [`Interaction`] and [`ComputedVisibility`] components, +/// If a UI node has both [`Interaction`] and [`ViewVisibility`] components, /// [`Interaction`] will always be [`Interaction::None`] -/// when [`ComputedVisibility::is_visible()`] is false. +/// when [`ViewVisibility::get()`] is false. /// This ensures that hidden UI nodes are not interactable, /// and do not end up stuck in an active state if hidden at the wrong time. /// @@ -125,12 +125,12 @@ pub struct NodeQuery { relative_cursor_position: Option<&'static mut RelativeCursorPosition>, focus_policy: Option<&'static FocusPolicy>, calculated_clip: Option<&'static CalculatedClip>, - computed_visibility: Option<&'static ComputedVisibility>, + view_visibility: Option<&'static ViewVisibility>, } /// The system that sets Interaction for all UI elements based on the mouse cursor activity /// -/// Entities with a hidden [`ComputedVisibility`] are always treated as released. +/// Entities with a hidden [`ViewVisibility`] are always treated as released. #[allow(clippy::too_many_arguments)] pub fn ui_focus_system( mut state: Local, @@ -204,8 +204,8 @@ pub fn ui_focus_system( .filter_map(|entity| { if let Ok(node) = node_query.get_mut(*entity) { // Nodes that are not rendered should not be interactable - if let Some(computed_visibility) = node.computed_visibility { - if !computed_visibility.is_visible() { + if let Some(view_visibility) = node.view_visibility { + if !view_visibility.get() { // Reset their interaction to None to avoid strange stuck state if let Some(mut interaction) = node.interaction { // We cannot simply set the interaction to None, as that will trigger change detection repeatedly diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index f43f4642d6b7d..6bbd5eebdf20d 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -10,8 +10,8 @@ use crate::{ use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; use bevy_render::{ - prelude::{Color, ComputedVisibility}, - view::Visibility, + prelude::Color, + view::{InheritedVisibility, ViewVisibility, Visibility}, }; use bevy_sprite::TextureAtlas; #[cfg(feature = "bevy_text")] @@ -46,8 +46,10 @@ pub struct NodeBundle { pub global_transform: GlobalTransform, /// Describes the visibility properties of the node pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, } @@ -64,7 +66,8 @@ impl Default for NodeBundle { transform: Default::default(), global_transform: Default::default(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), z_index: Default::default(), } } @@ -103,8 +106,10 @@ pub struct ImageBundle { pub global_transform: GlobalTransform, /// Describes the visibility properties of the node pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, } @@ -144,8 +149,10 @@ pub struct AtlasImageBundle { pub global_transform: GlobalTransform, /// Describes the visibility properties of the node pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, } @@ -180,8 +187,10 @@ pub struct TextBundle { pub global_transform: GlobalTransform, /// Describes the visibility properties of the node pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, /// The background color that will fill the containing node @@ -196,16 +205,17 @@ impl Default for TextBundle { text_layout_info: Default::default(), text_flags: Default::default(), calculated_size: Default::default(), - // Transparent background - background_color: BackgroundColor(Color::NONE), node: Default::default(), style: Default::default(), focus_policy: Default::default(), transform: Default::default(), global_transform: Default::default(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), z_index: Default::default(), + // Transparent background + background_color: BackgroundColor(Color::NONE), } } } @@ -291,8 +301,10 @@ pub struct ButtonBundle { pub global_transform: GlobalTransform, /// Describes the visibility properties of the node pub visibility: Visibility, + /// Inherited visibility of an entity. + pub inherited_visibility: InheritedVisibility, /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering - pub computed_visibility: ComputedVisibility, + pub view_visibility: ViewVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, } @@ -311,7 +323,8 @@ impl Default for ButtonBundle { transform: Default::default(), global_transform: Default::default(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), z_index: Default::default(), } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 794b2055715fb..cc3dbcedea672 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -4,6 +4,7 @@ mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; use bevy_ecs::storage::SparseSet; use bevy_hierarchy::Parent; +use bevy_render::view::ViewVisibility; use bevy_render::{ExtractSchedule, Render}; use bevy_window::{PrimaryWindow, Window}; pub use pipeline::*; @@ -27,7 +28,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderDevice, RenderQueue}, texture::Image, - view::{ComputedVisibility, ExtractedView, ViewUniforms}, + view::{ExtractedView, ViewUniforms}, Extract, RenderApp, RenderSet, }; use bevy_sprite::{SpriteAssetEvents, TextureAtlas}; @@ -177,7 +178,7 @@ pub fn extract_atlas_uinodes( &Node, &GlobalTransform, &BackgroundColor, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, &Handle, &UiTextureAtlasImage, @@ -192,14 +193,14 @@ pub fn extract_atlas_uinodes( uinode, transform, color, - visibility, + view_visibility, clip, texture_atlas_handle, atlas_image, )) = uinode_query.get(*entity) { // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { + if !view_visibility.get() || color.0.a() == 0.0 { continue; } @@ -279,7 +280,7 @@ pub fn extract_uinode_borders( &Style, &BorderColor, Option<&Parent>, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, ), Without, @@ -298,11 +299,11 @@ pub fn extract_uinode_borders( / ui_scale.0 as f32; for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((node, global_transform, style, border_color, parent, visibility, clip)) = + if let Ok((node, global_transform, style, border_color, parent, view_visibility, clip)) = uinode_query.get(*entity) { // Skip invisible borders - if !visibility.is_visible() + if !view_visibility.get() || border_color.0.a() == 0.0 || node.size().x <= 0. || node.size().y <= 0. @@ -400,7 +401,7 @@ pub fn extract_uinodes( &GlobalTransform, &BackgroundColor, Option<&UiImage>, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, ), Without, @@ -408,11 +409,11 @@ pub fn extract_uinodes( >, ) { for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((entity, uinode, transform, color, maybe_image, visibility, clip)) = + if let Ok((entity, uinode, transform, color, maybe_image, view_visibility, clip)) = uinode_query.get(*entity) { // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { + if !view_visibility.get() || color.0.a() == 0.0 { continue; } @@ -534,7 +535,7 @@ pub fn extract_text_uinodes( &GlobalTransform, &Text, &TextLayoutInfo, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, )>, >, @@ -549,11 +550,11 @@ pub fn extract_text_uinodes( let inverse_scale_factor = (scale_factor as f32).recip(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((uinode, global_transform, text, text_layout_info, visibility, clip)) = + if let Ok((uinode, global_transform, text, text_layout_info, view_visibility, clip)) = uinode_query.get(*entity) { // Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`) - if !visibility.is_visible() || uinode.size().x == 0. || uinode.size().y == 0. { + if !view_visibility.get() || uinode.size().x == 0. || uinode.size().y == 0. { continue; } let transform = global_transform.compute_matrix() diff --git a/errors/B0004.md b/errors/B0004.md index b55549625f451..55a2d3f8dab2d 100644 --- a/errors/B0004.md +++ b/errors/B0004.md @@ -7,7 +7,7 @@ without the hierarchy-inherited component in question. The hierarchy-inherited components defined in bevy include: -- [`ComputedVisibility`] +- [`InheritedVisibility`] - [`GlobalTransform`] Third party plugins may also define their own hierarchy components, so @@ -57,9 +57,9 @@ fn main() { This code **will not** show a cube on screen. This is because the entity spawned with `commands.spawn(…)` -doesn't have a [`ComputedVisibility`] component. +doesn't have a [`ViewVisibility`] or [`InheritedVisibility`] component. Since the cube is spawned as a child of an entity without the -[`ComputedVisibility`] component, it will not be visible at all. +visibility components, it will not be visible at all. To fix this, you must use [`SpatialBundle`] over [`TransformBundle`], as follows: @@ -74,7 +74,7 @@ fn setup_cube( ) { commands // We use SpatialBundle instead of TransformBundle, it contains the - // ComputedVisibility component needed to display the cube, + // visibility components needed to display the cube, // In addition to the Transform and GlobalTransform components. .spawn(SpatialBundle::default()) .with_children(|parent| { @@ -110,7 +110,8 @@ including when updating the [`Transform`] component of the child. You will most likely encounter this warning when loading a scene as a child of a pre-existing [`Entity`] that does not have the proper components. -[`ComputedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ComputedVisibility.html +[`InheritedVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.InheritedVisibility.html +[`ViewVisibility`]: https://docs.rs/bevy/*/bevy/render/view/struct.ViewVisibility.html [`GlobalTransform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.GlobalTransform.html [`Transform`]: https://docs.rs/bevy/*/bevy/transform/components/struct.Transform.html [`Parent`]: https://docs.rs/bevy/*/bevy/hierarchy/struct.Parent.html diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 2e0617488e7e1..5a9e0547751b8 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -294,11 +294,11 @@ pub fn extract_colored_mesh2d( mut previous_len: Local, // When extracting, you must use `Extract` to mark the `SystemParam`s // which should be taken from the main world. - query: Extract>>, + query: Extract>>, ) { let mut values = Vec::with_capacity(*previous_len); - for (entity, computed_visibility) in &query { - if !computed_visibility.is_visible() { + for (entity, view_visibility) in &query { + if !view_visibility.get() { continue; } values.push((entity, ColoredMesh2d)); diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index 20495b1ab7a61..f8194589b9328 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -170,7 +170,7 @@ fn move_camera(time: Res