@@ -169,13 +169,15 @@ pub struct VisibilityClass(pub SmallVec<[TypeId; 1]>);
169169/// Later in the frame, systems in [`CheckVisibility`] will mark any visible entities using [`ViewVisibility::set`].
170170/// Because of this, values of this type will be marked as changed every frame, even when they do not change.
171171///
172- /// If you wish to add custom visibility system that sets this value, make sure you add it to the [`CheckVisibility`] set.
172+ /// If you wish to add custom visibility system that sets this value, make sure you add it to the
173+ /// [`CheckVisibility`] set, and be sure to set [`WasVisibleNowHidden`] to false when `set`ting the
174+ /// [`ViewVisibility`]
173175///
174176/// [`VisibilityPropagate`]: VisibilitySystems::VisibilityPropagate
175177/// [`CheckVisibility`]: VisibilitySystems::CheckVisibility
176178#[ derive( Component , Deref , Debug , Default , Clone , Copy , Reflect , PartialEq , Eq ) ]
177179#[ reflect( Component , Default , Debug , PartialEq , Clone ) ]
178- #[ require( PreviouslyVisible ) ]
180+ #[ require( WasVisibleNowHidden ) ]
179181pub struct ViewVisibility ( bool ) ;
180182
181183impl ViewVisibility {
@@ -522,16 +524,28 @@ fn propagate_recursive(
522524 Ok ( ( ) )
523525}
524526
525- /// As systems that check visibility judge entities visible, they set this to `false`. Afterward,
526- /// the `mark_newly_hidden_entities_invisible` system runs and marks every entity still true as hidden.
527+ /// Used as a scratch space to ensure that [`ViewVisibility`] is only mutated (triggering change
528+ /// detection) when necessary.
529+ ///
530+ /// This is needed because an entity might be seen by many views (cameras, lights that cast shadows,
531+ /// etc.), so it is easy to know if an entity is visible to something, but hard to know if it is
532+ /// *globally* non-visible to any view. To solve this, we set this component to `true` every frame
533+ /// that the entity's `ViewVisibility` was also `true` the previous frame. Then, during the
534+ /// [`VisibilitySystems::CheckVisibility`] system set, it is the responsibility of those systems to
535+ /// mark this component `false` if the entity is currently visible. Once this is done, the only
536+ /// entities with this entity set to `true` are entities that were visible last frame, but are not
537+ /// visible this frame. Finally, we can use this to set the `ViewVisibility` of these entities to
538+ /// hidden, ensuring that we have only triggered change detection where necessary.
539+ ///
540+ /// Consider if we did the simplest approach of setting all entities to hidden, then marking visible
541+ /// entities. Every single [`ViwVisibility`] would trigger change detection.
527542#[ derive( Component , Default , Reflect , Deref , DerefMut ) ]
528543#[ reflect( Component ) ]
529- pub struct PreviouslyVisible ( bool ) ;
544+ pub struct WasVisibleNowHidden ( bool ) ;
530545
531- /// Resets the view visibility of every entity.
532- /// Entities that are visible will be marked as such later this frame
533- /// by a [`VisibilitySystems::CheckVisibility`] system.
534- fn reset_view_visibility ( mut query : Query < ( & ViewVisibility , & mut PreviouslyVisible ) > ) {
546+ /// Track entities that were visible last frame, used to granularly update [`ViewVisibility`] this
547+ /// frame. See [`WasVisibleNowHidden`] for details.
548+ fn reset_view_visibility ( mut query : Query < ( & ViewVisibility , & mut WasVisibleNowHidden ) > ) {
535549 query
536550 . par_iter_mut ( )
537551 . for_each ( |( view_visibility, mut previously_visible) | {
@@ -562,14 +576,14 @@ pub fn check_visibility(
562576 Entity ,
563577 & InheritedVisibility ,
564578 & mut ViewVisibility ,
579+ & mut WasVisibleNowHidden ,
565580 Option < & VisibilityClass > ,
566581 Option < & RenderLayers > ,
567582 Option < & Aabb > ,
568583 & GlobalTransform ,
569584 Has < NoFrustumCulling > ,
570585 Has < VisibilityRange > ,
571586 ) > ,
572- mut previously_visible : Query < & mut PreviouslyVisible > ,
573587 visible_entity_ranges : Option < Res < VisibleEntityRanges > > ,
574588) {
575589 let visible_entity_ranges = visible_entity_ranges. as_deref ( ) ;
@@ -590,6 +604,7 @@ pub fn check_visibility(
590604 entity,
591605 inherited_visibility,
592606 mut view_visibility,
607+ mut was_visible_now_hidden,
593608 visibility_class,
594609 maybe_entity_mask,
595610 maybe_model_aabb,
@@ -644,11 +659,13 @@ pub fn check_visibility(
644659 if !* * view_visibility {
645660 view_visibility. set ( ) ;
646661 }
662+ // The entity is visible, so we must set this to false.
663+ * * was_visible_now_hidden = false ;
647664
648665 // The visibility class may be None here because AABB gizmos can be enabled via
649666 // config without a renderable component being added to the entity. This workaround
650- // allows view visibility to be set for entities without a renderable component but
651- // that still need to render gizmos.
667+ // allows view visibility to be set for entities without a renderable component, but
668+ // still need to render gizmos.
652669 if let Some ( visibility_class) = visibility_class {
653670 // Add the entity to the queue for all visibility classes the entity is in.
654671 for visibility_class_id in visibility_class. iter ( ) {
@@ -663,19 +680,7 @@ pub fn check_visibility(
663680 // Drain all the thread queues into the `visible_entities` list.
664681 for class_queues in thread_queues. iter_mut ( ) {
665682 for ( class, entities) in class_queues {
666- let visible_entities_for_class = visible_entities. get_mut ( * class) ;
667- for entity in entities. drain ( ..) {
668- // As we mark entities as visible, we remove them from the
669- // `previous_visible_entities` list. At the end, all of the
670- // entities remaining in `previous_visible_entities` will be
671- // entities that were visible last frame but are no longer
672- // visible this frame.
673- if let Ok ( mut previously_visible) = previously_visible. get_mut ( entity) {
674- * * previously_visible = false ;
675- }
676-
677- visible_entities_for_class. push ( entity) ;
678- }
683+ visible_entities. get_mut ( * class) . append ( entities) ;
679684 }
680685 }
681686 }
@@ -689,7 +694,7 @@ pub fn check_visibility(
689694/// be invisible. This system goes through those entities and marks them newly
690695/// invisible (which sets the change flag for them).
691696fn mark_newly_hidden_entities_invisible (
692- mut view_visibilities : Query < ( & mut ViewVisibility , & mut PreviouslyVisible ) > ,
697+ mut view_visibilities : Query < ( & mut ViewVisibility , & mut WasVisibleNowHidden ) > ,
693698) {
694699 // Whatever previous visible entities are left are entities that were
695700 // visible last frame but just became invisible.
0 commit comments