From 02025eff0b25bb8c18103dbafc33a764e1989ad4 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 31 Aug 2023 15:52:59 -0700 Subject: [PATCH 1/2] Fix anonymous set name stack overflow (#9650) # Objective - Fixes #9641 - Anonymous sets are named by their system members. When `ScheduleBuildSettings::report_sets` is on, systems are named by their sets. So when getting the anonymous set name this would cause an infinite recursion. ## Solution - When getting the anonymous system set name, don't get their system's names with the sets the systems belong to. ## Other Possible solutions - An alternate solution might be to skip anonymous sets when getting the system's name for an anonymous set's name. --- crates/bevy_ecs/src/schedule/mod.rs | 33 ++++++++++++++++++++++++ crates/bevy_ecs/src/schedule/schedule.rs | 10 +++++-- 2 files changed, 41 insertions(+), 2 deletions(-) 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() ) From 02b520b4e85fa95f1f4a85656a677e3fc2155f36 Mon Sep 17 00:00:00 2001 From: Joseph <21144246+JoJoJet@users.noreply.github.com> Date: Fri, 1 Sep 2023 06:00:18 -0700 Subject: [PATCH 2/2] Split `ComputedVisibility` into two components to allow for accurate change detection and speed up visibility propagation (#9497) # Objective Fix #8267. Fixes half of #7840. The `ComputedVisibility` component contains two flags: hierarchy visibility, and view visibility (whether its visible to any cameras). Due to the modular and open-ended way that view visibility is computed, it triggers change detection every single frame, even when the value does not change. Since hierarchy visibility is stored in the same component as view visibility, this means that change detection for inherited visibility is completely broken. At the company I work for, this has become a real issue. We are using change detection to only re-render scenes when necessary. The broken state of change detection for computed visibility means that we have to to rely on the non-inherited `Visibility` component for now. This is workable in the early stages of our project, but since we will inevitably want to use the hierarchy, we will have to either: 1. Roll our own solution for computed visibility. 2. Fix the issue for everyone. ## Solution Split the `ComputedVisibility` component into two: `InheritedVisibilty` and `ViewVisibility`. This allows change detection to behave properly for `InheritedVisibility`. View visiblity is still erratic, although it is less useful to be able to detect changes for this flavor of visibility. Overall, this actually simplifies the API. Since the visibility system consists of self-explaining components, it is much easier to document the behavior and usage. This approach is more modular and "ECS-like" -- one could strip out the `ViewVisibility` component entirely if it's not needed, and rely only on inherited visibility. --- ## Changelog - `ComputedVisibility` has been removed in favor of: `InheritedVisibility` and `ViewVisiblity`. ## Migration Guide The `ComputedVisibilty` component has been split into `InheritedVisiblity` and `ViewVisibility`. Replace any usages of `ComputedVisibility::is_visible_in_hierarchy` with `InheritedVisibility::get`, and replace `ComputedVisibility::is_visible_in_view` with `ViewVisibility::get`. ```rust // Before: commands.spawn(VisibilityBundle { visibility: Visibility::Inherited, computed_visibility: ComputedVisibility::default(), }); // After: commands.spawn(VisibilityBundle { visibility: Visibility::Inherited, inherited_visibility: InheritedVisibility::default(), view_visibility: ViewVisibility::default(), }); ``` ```rust // Before: fn my_system(q: Query<&ComputedVisibilty>) { for vis in &q { if vis.is_visible_in_hierarchy() { // After: fn my_system(q: Query<&InheritedVisibility>) { for inherited_visibility in &q { if inherited_visibility.get() { ``` ```rust // Before: fn my_system(q: Query<&ComputedVisibilty>) { for vis in &q { if vis.is_visible_in_view() { // After: fn my_system(q: Query<&ViewVisibility>) { for view_visibility in &q { if view_visibility.get() { ``` ```rust // Before: fn my_system(mut q: Query<&mut ComputedVisibilty>) { for vis in &mut q { vis.set_visible_in_view(); // After: fn my_system(mut q: Query<&mut ViewVisibility>) { for view_visibility in &mut q { view_visibility.set(); ``` --------- Co-authored-by: Robert Swain --- .../src/valid_parent_check_plugin.rs | 2 +- crates/bevy_pbr/src/bundle.rs | 21 +- crates/bevy_pbr/src/lib.rs | 4 +- crates/bevy_pbr/src/light.rs | 64 +-- crates/bevy_pbr/src/render/light.rs | 24 +- crates/bevy_pbr/src/render/mesh.rs | 12 +- crates/bevy_pbr/src/render/morph.rs | 8 +- crates/bevy_render/src/extract_component.rs | 8 +- crates/bevy_render/src/lib.rs | 4 +- crates/bevy_render/src/spatial_bundle.rs | 15 +- crates/bevy_render/src/view/mod.rs | 4 +- crates/bevy_render/src/view/visibility/mod.rs | 449 +++++++++++------- crates/bevy_scene/src/bundle.rs | 10 +- crates/bevy_sprite/src/bundle.rs | 12 +- crates/bevy_sprite/src/mesh2d/material.rs | 11 +- crates/bevy_sprite/src/mesh2d/mesh.rs | 8 +- crates/bevy_sprite/src/render/mod.rs | 18 +- crates/bevy_text/src/text2d.rs | 16 +- crates/bevy_ui/src/focus.rs | 14 +- crates/bevy_ui/src/node_bundles.rs | 37 +- crates/bevy_ui/src/render/mod.rs | 27 +- errors/B0004.md | 11 +- examples/2d/mesh2d_manual.rs | 6 +- examples/stress_tests/many_cubes.rs | 4 +- 24 files changed, 472 insertions(+), 317 deletions(-) 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 4d8f31fc8abeb..33021e8413f93 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -219,7 +219,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) @@ -241,7 +241,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 d8469f527a4be..e18e9d4acae4a 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 a52c54557ed82..e9abd14609851 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -37,7 +37,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; @@ -261,7 +261,7 @@ pub fn extract_meshes( meshes_query: Extract< Query<( Entity, - &ComputedVisibility, + &ViewVisibility, &GlobalTransform, Option<&PreviousGlobalTransform>, &Handle, @@ -272,7 +272,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 @@ -354,7 +354,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>, ) { @@ -362,8 +362,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 473f75fc3aba4..8f70b5721c303 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -38,7 +38,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, }; } @@ -357,7 +357,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 487c3ef415479..322aaf35fdd3b 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -40,8 +40,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 092e5a302bb8b..1e88c342266d7 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, DEFAULT_IMAGE_HANDLE}, - 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, } impl Default for SpriteBundle { @@ -30,7 +32,8 @@ impl Default for SpriteBundle { global_transform: Default::default(), texture: DEFAULT_IMAGE_HANDLE.typed(), visibility: Default::default(), - computed_visibility: Default::default(), + inherited_visibility: Default::default(), + view_visibility: Default::default(), } } } @@ -47,6 +50,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 8e7ca92bf79da..98fd7791935df 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -31,7 +31,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 e614c58e8495e..8489d152fa427 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, }; @@ -139,11 +139,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 1468e30a72998..b92acaa51ee60 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, }; @@ -349,7 +349,7 @@ pub fn extract_sprites( sprite_query: Extract< Query<( Entity, - &ComputedVisibility, + &ViewVisibility, &Sprite, &GlobalTransform, &Handle, @@ -358,15 +358,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 @@ -385,8 +385,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 602e4c449c27e..79fcc40094fc0 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -14,7 +14,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}; @@ -70,8 +70,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, } @@ -83,7 +85,7 @@ pub fn extract_text2d_sprite( windows: Extract>>, text2d_query: Extract< Query<( - &ComputedVisibility, + &ViewVisibility, &Text, &TextLayoutInfo, &Anchor, @@ -98,10 +100,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 1305093c05047..2f4229b5b83f5 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::*; @@ -29,7 +30,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; @@ -182,7 +183,7 @@ pub fn extract_atlas_uinodes( &Node, &GlobalTransform, &BackgroundColor, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, &Handle, &UiTextureAtlasImage, @@ -197,14 +198,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; } @@ -284,7 +285,7 @@ pub fn extract_uinode_borders( &Style, &BorderColor, Option<&Parent>, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, ), Without, @@ -303,11 +304,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. @@ -405,7 +406,7 @@ pub fn extract_uinodes( &GlobalTransform, &BackgroundColor, Option<&UiImage>, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, ), Without, @@ -413,11 +414,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; } @@ -539,7 +540,7 @@ pub fn extract_text_uinodes( &GlobalTransform, &Text, &TextLayoutInfo, - &ComputedVisibility, + &ViewVisibility, Option<&CalculatedClip>, )>, >, @@ -554,11 +555,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 b5fa7c306303e..7a3ee4a9d68d8 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -296,11 +296,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