Skip to content

Commit dd364e6

Browse files
committed
Try to make visibility easier to grok
1 parent b4d06ef commit dd364e6

File tree

2 files changed

+43
-52
lines changed

2 files changed

+43
-52
lines changed

crates/bevy_camera/src/visibility/mod.rs

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
179181
pub struct ViewVisibility(bool);
180182

181183
impl 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).
691696
fn 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.

crates/bevy_light/src/lib.rs

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use cluster::{
2626
};
2727
mod ambient_light;
2828
pub use ambient_light::{AmbientLight, GlobalAmbientLight};
29-
use bevy_camera::visibility::PreviouslyVisible;
29+
use bevy_camera::visibility::WasVisibleNowHidden;
3030

3131
mod probe;
3232
pub use probe::{
@@ -452,17 +452,15 @@ pub fn check_dir_light_mesh_visibility(
452452
// TODO: use resource to avoid unnecessary memory alloc
453453
let mut defer_queue = core::mem::take(defer_visible_entities_queue.deref_mut());
454454
commands.queue(move |world: &mut World| {
455-
let mut query = world.query::<(&mut ViewVisibility, &mut PreviouslyVisible)>();
455+
let mut query = world.query::<(&mut ViewVisibility, &mut WasVisibleNowHidden)>();
456456
for entities in defer_queue.iter_mut() {
457457
let mut iter = query.iter_many_mut(world, entities.iter());
458-
while let Some((mut view_visibility, mut previously_visible)) = iter.fetch_next() {
458+
while let Some((mut view_visibility, mut was_visible_now_hidden)) = iter.fetch_next() {
459459
if !**view_visibility {
460460
view_visibility.set();
461461
}
462-
463-
// Remove any entities that were discovered to be
464-
// visible from the `PreviousVisibleEntities` resource.
465-
**previously_visible = false;
462+
// Unset any entities that were discovered to be visible
463+
**was_visible_now_hidden = false;
466464
}
467465
}
468466
});
@@ -489,6 +487,7 @@ pub fn check_point_light_mesh_visibility(
489487
Entity,
490488
&InheritedVisibility,
491489
&mut ViewVisibility,
490+
&mut WasVisibleNowHidden,
492491
Option<&RenderLayers>,
493492
Option<&Aabb>,
494493
Option<&GlobalTransform>,
@@ -502,13 +501,11 @@ pub fn check_point_light_mesh_visibility(
502501
),
503502
>,
504503
visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
505-
mut previously_visible: Query<&mut PreviouslyVisible>,
506504
mut cubemap_visible_entities_queue: Local<Parallel<[Vec<Entity>; 6]>>,
507505
mut spot_visible_entities_queue: Local<Parallel<Vec<Entity>>>,
508506
mut checked_lights: Local<EntityHashSet>,
509507
) {
510508
checked_lights.clear();
511-
let previously_visible = &mut previously_visible;
512509

513510
let visible_entity_ranges = visible_entity_ranges.as_deref();
514511
for visible_lights in &visible_point_lights {
@@ -548,6 +545,7 @@ pub fn check_point_light_mesh_visibility(
548545
entity,
549546
inherited_visibility,
550547
mut view_visibility,
548+
mut was_visible_now_hidden,
551549
maybe_entity_mask,
552550
maybe_aabb,
553551
maybe_transform,
@@ -589,13 +587,15 @@ pub fn check_point_light_mesh_visibility(
589587
if !**view_visibility {
590588
view_visibility.set();
591589
}
590+
**was_visible_now_hidden = false;
592591
visible_entities.push(entity);
593592
}
594593
}
595594
} else {
596595
if !**view_visibility {
597596
view_visibility.set();
598597
}
598+
**was_visible_now_hidden = false;
599599
for visible_entities in cubemap_visible_entities_local_queue.iter_mut()
600600
{
601601
visible_entities.push(entity);
@@ -608,15 +608,6 @@ pub fn check_point_light_mesh_visibility(
608608
for (dst, source) in
609609
cubemap_visible_entities.iter_mut().zip(entities.iter_mut())
610610
{
611-
// Remove any entities that were discovered to be
612-
// visible from the `PreviousVisibleEntities` resource.
613-
for entity in source.iter() {
614-
if let Ok(mut previously_visible) = previously_visible.get_mut(*entity)
615-
{
616-
**previously_visible = false;
617-
}
618-
}
619-
620611
dst.entities.append(source);
621612
}
622613
}
@@ -650,6 +641,7 @@ pub fn check_point_light_mesh_visibility(
650641
entity,
651642
inherited_visibility,
652643
mut view_visibility,
644+
mut was_visible_now_hidden,
653645
maybe_entity_mask,
654646
maybe_aabb,
655647
maybe_transform,
@@ -688,27 +680,21 @@ pub fn check_point_light_mesh_visibility(
688680
if !**view_visibility {
689681
view_visibility.set();
690682
}
683+
**was_visible_now_hidden = false;
691684
spot_visible_entities_local_queue.push(entity);
692685
}
693686
} else {
694687
if !**view_visibility {
695688
view_visibility.set();
696689
}
690+
**was_visible_now_hidden = false;
697691
spot_visible_entities_local_queue.push(entity);
698692
}
699693
},
700694
);
701695

702696
for entities in spot_visible_entities_queue.iter_mut() {
703697
visible_entities.append(entities);
704-
705-
// Remove any entities that were discovered to be visible
706-
// from the `PreviousVisibleEntities` resource.
707-
for entity in entities.iter() {
708-
if let Ok(mut previously_visible) = previously_visible.get_mut(*entity) {
709-
**previously_visible = false;
710-
}
711-
}
712698
}
713699

714700
shrink_entities(visible_entities.deref_mut());

0 commit comments

Comments
 (0)