1mod range;
2mod render_layers;
3
4use core::any::TypeId;
5
6use bevy_ecs::component::HookContext;
7use bevy_ecs::entity::EntityHashSet;
8use bevy_ecs::world::DeferredWorld;
9use derive_more::derive::{Deref, DerefMut};
10pub use range::*;
11pub use render_layers::*;
12
13use bevy_app::{Plugin, PostUpdate};
14use bevy_asset::Assets;
15use bevy_ecs::{hierarchy::validate_parent_has_component, prelude::*};
16use bevy_reflect::{std_traits::ReflectDefault, Reflect};
17use bevy_transform::{components::GlobalTransform, TransformSystem};
18use bevy_utils::{Parallel, TypeIdMap};
19use smallvec::SmallVec;
20
21use super::NoCpuCulling;
22use crate::{
23 camera::{Camera, CameraProjection, Projection},
24 mesh::{Mesh, Mesh3d, MeshAabb},
25 primitives::{Aabb, Frustum, Sphere},
26 sync_world::MainEntity,
27};
28
29#[derive(Component, Clone, Copy, Reflect, Debug, PartialEq, Eq, Default)]
37#[reflect(Component, Default, Debug, PartialEq, Clone)]
38#[require(InheritedVisibility, ViewVisibility)]
39pub enum Visibility {
40 #[default]
44 Inherited,
45 Hidden,
47 Visible,
52}
53
54impl Visibility {
55 #[inline]
58 pub fn toggle_inherited_visible(&mut self) {
59 *self = match *self {
60 Visibility::Inherited => Visibility::Visible,
61 Visibility::Visible => Visibility::Inherited,
62 _ => *self,
63 };
64 }
65 #[inline]
68 pub fn toggle_inherited_hidden(&mut self) {
69 *self = match *self {
70 Visibility::Inherited => Visibility::Hidden,
71 Visibility::Hidden => Visibility::Inherited,
72 _ => *self,
73 };
74 }
75 #[inline]
78 pub fn toggle_visible_hidden(&mut self) {
79 *self = match *self {
80 Visibility::Visible => Visibility::Hidden,
81 Visibility::Hidden => Visibility::Visible,
82 _ => *self,
83 };
84 }
85}
86
87impl PartialEq<Visibility> for &Visibility {
89 #[inline]
90 fn eq(&self, other: &Visibility) -> bool {
91 <Visibility as PartialEq<Visibility>>::eq(*self, other)
93 }
94}
95
96impl PartialEq<&Visibility> for Visibility {
98 #[inline]
99 fn eq(&self, other: &&Visibility) -> bool {
100 <Visibility as PartialEq<Visibility>>::eq(self, *other)
102 }
103}
104
105#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
112#[reflect(Component, Default, Debug, PartialEq, Clone)]
113#[component(on_insert = validate_parent_has_component::<Self>)]
114pub struct InheritedVisibility(bool);
115
116impl InheritedVisibility {
117 pub const HIDDEN: Self = Self(false);
119 pub const VISIBLE: Self = Self(true);
121
122 #[inline]
125 pub fn get(self) -> bool {
126 self.0
127 }
128}
129
130#[derive(Clone, Component, Default, Reflect, Deref, DerefMut)]
155#[reflect(Component, Default, Clone)]
156pub struct VisibilityClass(pub SmallVec<[TypeId; 1]>);
157
158#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)]
169#[reflect(Component, Default, Debug, PartialEq, Clone)]
170pub struct ViewVisibility(bool);
171
172impl ViewVisibility {
173 pub const HIDDEN: Self = Self(false);
175
176 #[inline]
179 pub fn get(self) -> bool {
180 self.0
181 }
182
183 #[inline]
196 pub fn set(&mut self) {
197 self.0 = true;
198 }
199}
200
201#[derive(Debug, Component, Default, Reflect)]
209#[reflect(Component, Default, Debug)]
210pub struct NoFrustumCulling;
211
212#[derive(Clone, Component, Default, Debug, Reflect)]
222#[reflect(Component, Default, Debug, Clone)]
223pub struct VisibleEntities {
224 #[reflect(ignore, clone)]
225 pub entities: TypeIdMap<Vec<Entity>>,
226}
227
228impl VisibleEntities {
229 pub fn get(&self, type_id: TypeId) -> &[Entity] {
230 match self.entities.get(&type_id) {
231 Some(entities) => &entities[..],
232 None => &[],
233 }
234 }
235
236 pub fn get_mut(&mut self, type_id: TypeId) -> &mut Vec<Entity> {
237 self.entities.entry(type_id).or_default()
238 }
239
240 pub fn iter(&self, type_id: TypeId) -> impl DoubleEndedIterator<Item = &Entity> {
241 self.get(type_id).iter()
242 }
243
244 pub fn len(&self, type_id: TypeId) -> usize {
245 self.get(type_id).len()
246 }
247
248 pub fn is_empty(&self, type_id: TypeId) -> bool {
249 self.get(type_id).is_empty()
250 }
251
252 pub fn clear(&mut self, type_id: TypeId) {
253 self.get_mut(type_id).clear();
254 }
255
256 pub fn clear_all(&mut self) {
257 for entities in self.entities.values_mut() {
259 entities.clear();
260 }
261 }
262
263 pub fn push(&mut self, entity: Entity, type_id: TypeId) {
264 self.get_mut(type_id).push(entity);
265 }
266}
267
268#[derive(Clone, Component, Default, Debug, Reflect)]
272#[reflect(Component, Default, Debug, Clone)]
273pub struct RenderVisibleEntities {
274 #[reflect(ignore, clone)]
275 pub entities: TypeIdMap<Vec<(Entity, MainEntity)>>,
276}
277
278impl RenderVisibleEntities {
279 pub fn get<QF>(&self) -> &[(Entity, MainEntity)]
280 where
281 QF: 'static,
282 {
283 match self.entities.get(&TypeId::of::<QF>()) {
284 Some(entities) => &entities[..],
285 None => &[],
286 }
287 }
288
289 pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &(Entity, MainEntity)>
290 where
291 QF: 'static,
292 {
293 self.get::<QF>().iter()
294 }
295
296 pub fn len<QF>(&self) -> usize
297 where
298 QF: 'static,
299 {
300 self.get::<QF>().len()
301 }
302
303 pub fn is_empty<QF>(&self) -> bool
304 where
305 QF: 'static,
306 {
307 self.get::<QF>().is_empty()
308 }
309}
310
311#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
312pub enum VisibilitySystems {
313 CalculateBounds,
316 UpdateFrusta,
318 VisibilityPropagate,
321 CheckVisibility,
328 MarkNewlyHiddenEntitiesInvisible,
332}
333
334pub struct VisibilityPlugin;
335
336impl Plugin for VisibilityPlugin {
337 fn build(&self, app: &mut bevy_app::App) {
338 use VisibilitySystems::*;
339
340 app.register_type::<VisibilityClass>()
341 .configure_sets(
342 PostUpdate,
343 (CalculateBounds, UpdateFrusta, VisibilityPropagate)
344 .before(CheckVisibility)
345 .after(TransformSystem::TransformPropagate),
346 )
347 .configure_sets(
348 PostUpdate,
349 MarkNewlyHiddenEntitiesInvisible.after(CheckVisibility),
350 )
351 .init_resource::<PreviousVisibleEntities>()
352 .add_systems(
353 PostUpdate,
354 (
355 calculate_bounds.in_set(CalculateBounds),
356 (visibility_propagate_system, reset_view_visibility)
357 .in_set(VisibilityPropagate),
358 check_visibility.in_set(CheckVisibility),
359 mark_newly_hidden_entities_invisible.in_set(MarkNewlyHiddenEntitiesInvisible),
360 ),
361 );
362 }
363}
364
365pub fn calculate_bounds(
370 mut commands: Commands,
371 meshes: Res<Assets<Mesh>>,
372 without_aabb: Query<(Entity, &Mesh3d), (Without<Aabb>, Without<NoFrustumCulling>)>,
373) {
374 for (entity, mesh_handle) in &without_aabb {
375 if let Some(mesh) = meshes.get(mesh_handle) {
376 if let Some(aabb) = mesh.compute_aabb() {
377 commands.entity(entity).try_insert(aabb);
378 }
379 }
380 }
381}
382
383pub fn update_frusta(
387 mut views: Query<
388 (&GlobalTransform, &Projection, &mut Frustum),
389 Or<(Changed<GlobalTransform>, Changed<Projection>)>,
390 >,
391) {
392 for (transform, projection, mut frustum) in &mut views {
393 *frustum = projection.compute_frustum(transform);
394 }
395}
396
397fn visibility_propagate_system(
398 changed: Query<
399 (Entity, &Visibility, Option<&ChildOf>, Option<&Children>),
400 (
401 With<InheritedVisibility>,
402 Or<(Changed<Visibility>, Changed<ChildOf>)>,
403 ),
404 >,
405 mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,
406 children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
407) {
408 for (entity, visibility, child_of, children) in &changed {
409 let is_visible = match visibility {
410 Visibility::Visible => true,
411 Visibility::Hidden => false,
412 Visibility::Inherited => child_of
414 .and_then(|c| visibility_query.get(c.parent()).ok())
415 .is_none_or(|(_, x)| x.get()),
416 };
417 let (_, mut inherited_visibility) = visibility_query
418 .get_mut(entity)
419 .expect("With<InheritedVisibility> ensures this query will return a value");
420
421 if inherited_visibility.get() != is_visible {
425 inherited_visibility.0 = is_visible;
426
427 for &child in children.into_iter().flatten() {
429 let _ =
430 propagate_recursive(is_visible, child, &mut visibility_query, &children_query);
431 }
432 }
433 }
434}
435
436fn propagate_recursive(
437 parent_is_visible: bool,
438 entity: Entity,
439 visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,
440 children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
441 ) -> Result<(), ()> {
444 let (visibility, mut inherited_visibility) = visibility_query.get_mut(entity).map_err(drop)?;
447
448 let is_visible = match visibility {
449 Visibility::Visible => true,
450 Visibility::Hidden => false,
451 Visibility::Inherited => parent_is_visible,
452 };
453
454 if inherited_visibility.get() != is_visible {
456 inherited_visibility.0 = is_visible;
457
458 for &child in children_query.get(entity).ok().into_iter().flatten() {
460 let _ = propagate_recursive(is_visible, child, visibility_query, children_query);
461 }
462 }
463
464 Ok(())
465}
466
467#[derive(Resource, Default, Deref, DerefMut)]
473pub struct PreviousVisibleEntities(EntityHashSet);
474
475fn reset_view_visibility(
479 mut query: Query<(Entity, &ViewVisibility)>,
480 mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
481) {
482 previous_visible_entities.clear();
483
484 query.iter_mut().for_each(|(entity, view_visibility)| {
485 if view_visibility.get() {
487 previous_visible_entities.insert(entity);
488 }
489 });
490}
491
492pub fn check_visibility(
501 mut thread_queues: Local<Parallel<TypeIdMap<Vec<Entity>>>>,
502 mut view_query: Query<(
503 Entity,
504 &mut VisibleEntities,
505 &Frustum,
506 Option<&RenderLayers>,
507 &Camera,
508 Has<NoCpuCulling>,
509 )>,
510 mut visible_aabb_query: Query<(
511 Entity,
512 &InheritedVisibility,
513 &mut ViewVisibility,
514 &VisibilityClass,
515 Option<&RenderLayers>,
516 Option<&Aabb>,
517 &GlobalTransform,
518 Has<NoFrustumCulling>,
519 Has<VisibilityRange>,
520 )>,
521 visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
522 mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
523) {
524 let visible_entity_ranges = visible_entity_ranges.as_deref();
525
526 for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
527 &mut view_query
528 {
529 if !camera.is_active {
530 continue;
531 }
532
533 let view_mask = maybe_view_mask.unwrap_or_default();
534
535 visible_aabb_query.par_iter_mut().for_each_init(
536 || thread_queues.borrow_local_mut(),
537 |queue, query_item| {
538 let (
539 entity,
540 inherited_visibility,
541 mut view_visibility,
542 visibility_class,
543 maybe_entity_mask,
544 maybe_model_aabb,
545 transform,
546 no_frustum_culling,
547 has_visibility_range,
548 ) = query_item;
549
550 if !inherited_visibility.get() {
553 return;
554 }
555
556 let entity_mask = maybe_entity_mask.unwrap_or_default();
557 if !view_mask.intersects(entity_mask) {
558 return;
559 }
560
561 if has_visibility_range
563 && visible_entity_ranges.is_some_and(|visible_entity_ranges| {
564 !visible_entity_ranges.entity_is_in_range_of_view(entity, view)
565 })
566 {
567 return;
568 }
569
570 if !no_frustum_culling && !no_cpu_culling {
572 if let Some(model_aabb) = maybe_model_aabb {
573 let world_from_local = transform.affine();
574 let model_sphere = Sphere {
575 center: world_from_local.transform_point3a(model_aabb.center),
576 radius: transform.radius_vec3a(model_aabb.half_extents),
577 };
578 if !frustum.intersects_sphere(&model_sphere, false) {
580 return;
581 }
582 if !frustum.intersects_obb(model_aabb, &world_from_local, true, false) {
584 return;
585 }
586 }
587 }
588
589 if !**view_visibility {
593 view_visibility.set();
594 }
595
596 for visibility_class_id in visibility_class.iter() {
599 queue.entry(*visibility_class_id).or_default().push(entity);
600 }
601 },
602 );
603
604 visible_entities.clear_all();
605
606 for class_queues in thread_queues.iter_mut() {
608 for (class, entities) in class_queues {
609 let visible_entities_for_class = visible_entities.get_mut(*class);
610 for entity in entities.drain(..) {
611 previous_visible_entities.remove(&entity);
617
618 visible_entities_for_class.push(entity);
619 }
620 }
621 }
622 }
623}
624
625fn mark_newly_hidden_entities_invisible(
633 mut view_visibilities: Query<&mut ViewVisibility>,
634 mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
635) {
636 for entity in previous_visible_entities.drain() {
639 if let Ok(mut view_visibility) = view_visibilities.get_mut(entity) {
640 *view_visibility = ViewVisibility::HIDDEN;
641 }
642 }
643}
644
645pub fn add_visibility_class<C>(
659 mut world: DeferredWorld<'_>,
660 HookContext { entity, .. }: HookContext,
661) where
662 C: 'static,
663{
664 if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {
665 visibility_class.push(TypeId::of::<C>());
666 }
667}
668
669#[cfg(test)]
670mod test {
671 use super::*;
672 use bevy_app::prelude::*;
673
674 #[test]
675 fn visibility_propagation() {
676 let mut app = App::new();
677 app.add_systems(Update, visibility_propagate_system);
678
679 let root1 = app.world_mut().spawn(Visibility::Hidden).id();
680 let root1_child1 = app.world_mut().spawn(Visibility::default()).id();
681 let root1_child2 = app.world_mut().spawn(Visibility::Hidden).id();
682 let root1_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
683 let root1_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
684
685 app.world_mut()
686 .entity_mut(root1)
687 .add_children(&[root1_child1, root1_child2]);
688 app.world_mut()
689 .entity_mut(root1_child1)
690 .add_children(&[root1_child1_grandchild1]);
691 app.world_mut()
692 .entity_mut(root1_child2)
693 .add_children(&[root1_child2_grandchild1]);
694
695 let root2 = app.world_mut().spawn(Visibility::default()).id();
696 let root2_child1 = app.world_mut().spawn(Visibility::default()).id();
697 let root2_child2 = app.world_mut().spawn(Visibility::Hidden).id();
698 let root2_child1_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
699 let root2_child2_grandchild1 = app.world_mut().spawn(Visibility::default()).id();
700
701 app.world_mut()
702 .entity_mut(root2)
703 .add_children(&[root2_child1, root2_child2]);
704 app.world_mut()
705 .entity_mut(root2_child1)
706 .add_children(&[root2_child1_grandchild1]);
707 app.world_mut()
708 .entity_mut(root2_child2)
709 .add_children(&[root2_child2_grandchild1]);
710
711 app.update();
712
713 let is_visible = |e: Entity| {
714 app.world()
715 .entity(e)
716 .get::<InheritedVisibility>()
717 .unwrap()
718 .get()
719 };
720 assert!(
721 !is_visible(root1),
722 "invisibility propagates down tree from root"
723 );
724 assert!(
725 !is_visible(root1_child1),
726 "invisibility propagates down tree from root"
727 );
728 assert!(
729 !is_visible(root1_child2),
730 "invisibility propagates down tree from root"
731 );
732 assert!(
733 !is_visible(root1_child1_grandchild1),
734 "invisibility propagates down tree from root"
735 );
736 assert!(
737 !is_visible(root1_child2_grandchild1),
738 "invisibility propagates down tree from root"
739 );
740
741 assert!(
742 is_visible(root2),
743 "visibility propagates down tree from root"
744 );
745 assert!(
746 is_visible(root2_child1),
747 "visibility propagates down tree from root"
748 );
749 assert!(
750 !is_visible(root2_child2),
751 "visibility propagates down tree from root, but local invisibility is preserved"
752 );
753 assert!(
754 is_visible(root2_child1_grandchild1),
755 "visibility propagates down tree from root"
756 );
757 assert!(
758 !is_visible(root2_child2_grandchild1),
759 "child's invisibility propagates down to grandchild"
760 );
761 }
762
763 #[test]
764 fn test_visibility_propagation_on_parent_change() {
765 let mut app = App::new();
767
768 app.add_systems(Update, visibility_propagate_system);
769
770 let parent1 = app.world_mut().spawn((Visibility::Hidden,)).id();
772 let parent2 = app.world_mut().spawn((Visibility::Visible,)).id();
773 let child1 = app.world_mut().spawn((Visibility::Inherited,)).id();
774 let child2 = app.world_mut().spawn((Visibility::Inherited,)).id();
775
776 app.world_mut()
778 .entity_mut(parent1)
779 .add_children(&[child1, child2]);
780
781 app.update();
783
784 app.world_mut()
786 .entity_mut(parent2)
787 .insert(Visibility::Visible);
788 app.world_mut().entity_mut(child2).insert(ChildOf(parent2)); app.update();
793
794 let is_visible = |e: Entity| {
795 app.world()
796 .entity(e)
797 .get::<InheritedVisibility>()
798 .unwrap()
799 .get()
800 };
801
802 assert!(
805 !is_visible(child1),
806 "Child1 should inherit visibility from parent"
807 );
808
809 assert!(
810 is_visible(child2),
811 "Child2 should inherit visibility from parent"
812 );
813 }
814
815 #[test]
816 fn visibility_propagation_unconditional_visible() {
817 use Visibility::{Hidden, Inherited, Visible};
818
819 let mut app = App::new();
820 app.add_systems(Update, visibility_propagate_system);
821
822 let root1 = app.world_mut().spawn(Visible).id();
823 let root1_child1 = app.world_mut().spawn(Inherited).id();
824 let root1_child2 = app.world_mut().spawn(Hidden).id();
825 let root1_child1_grandchild1 = app.world_mut().spawn(Visible).id();
826 let root1_child2_grandchild1 = app.world_mut().spawn(Visible).id();
827
828 let root2 = app.world_mut().spawn(Inherited).id();
829 let root3 = app.world_mut().spawn(Hidden).id();
830
831 app.world_mut()
832 .entity_mut(root1)
833 .add_children(&[root1_child1, root1_child2]);
834 app.world_mut()
835 .entity_mut(root1_child1)
836 .add_children(&[root1_child1_grandchild1]);
837 app.world_mut()
838 .entity_mut(root1_child2)
839 .add_children(&[root1_child2_grandchild1]);
840
841 app.update();
842
843 let is_visible = |e: Entity| {
844 app.world()
845 .entity(e)
846 .get::<InheritedVisibility>()
847 .unwrap()
848 .get()
849 };
850 assert!(
851 is_visible(root1),
852 "an unconditionally visible root is visible"
853 );
854 assert!(
855 is_visible(root1_child1),
856 "an inheriting child of an unconditionally visible parent is visible"
857 );
858 assert!(
859 !is_visible(root1_child2),
860 "a hidden child on an unconditionally visible parent is hidden"
861 );
862 assert!(
863 is_visible(root1_child1_grandchild1),
864 "an unconditionally visible child of an inheriting parent is visible"
865 );
866 assert!(
867 is_visible(root1_child2_grandchild1),
868 "an unconditionally visible child of a hidden parent is visible"
869 );
870 assert!(is_visible(root2), "an inheriting root is visible");
871 assert!(!is_visible(root3), "a hidden root is hidden");
872 }
873
874 #[test]
875 fn visibility_propagation_change_detection() {
876 let mut world = World::new();
877 let mut schedule = Schedule::default();
878 schedule.add_systems(visibility_propagate_system);
879
880 let id1 = world.spawn(Visibility::default()).id();
883
884 let id2 = world.spawn(Visibility::default()).id();
885 world.entity_mut(id1).add_children(&[id2]);
886
887 let id3 = world.spawn(Visibility::Hidden).id();
888 world.entity_mut(id2).add_children(&[id3]);
889
890 let id4 = world.spawn(Visibility::default()).id();
891 world.entity_mut(id3).add_children(&[id4]);
892
893 schedule.run(&mut world);
897 world.clear_trackers();
898
899 let mut q = world.query::<Ref<InheritedVisibility>>();
900
901 assert!(!q.get(&world, id1).unwrap().is_changed());
902 assert!(!q.get(&world, id2).unwrap().is_changed());
903 assert!(!q.get(&world, id3).unwrap().is_changed());
904 assert!(!q.get(&world, id4).unwrap().is_changed());
905
906 world.clear_trackers();
907 world.entity_mut(id1).insert(Visibility::Hidden);
908 schedule.run(&mut world);
909
910 assert!(q.get(&world, id1).unwrap().is_changed());
911 assert!(q.get(&world, id2).unwrap().is_changed());
912 assert!(!q.get(&world, id3).unwrap().is_changed());
913 assert!(!q.get(&world, id4).unwrap().is_changed());
914
915 world.clear_trackers();
916 schedule.run(&mut world);
917
918 assert!(!q.get(&world, id1).unwrap().is_changed());
919 assert!(!q.get(&world, id2).unwrap().is_changed());
920 assert!(!q.get(&world, id3).unwrap().is_changed());
921 assert!(!q.get(&world, id4).unwrap().is_changed());
922
923 world.clear_trackers();
924 world.entity_mut(id3).insert(Visibility::Inherited);
925 schedule.run(&mut world);
926
927 assert!(!q.get(&world, id1).unwrap().is_changed());
928 assert!(!q.get(&world, id2).unwrap().is_changed());
929 assert!(!q.get(&world, id3).unwrap().is_changed());
930 assert!(!q.get(&world, id4).unwrap().is_changed());
931
932 world.clear_trackers();
933 world.entity_mut(id2).insert(Visibility::Visible);
934 schedule.run(&mut world);
935
936 assert!(!q.get(&world, id1).unwrap().is_changed());
937 assert!(q.get(&world, id2).unwrap().is_changed());
938 assert!(q.get(&world, id3).unwrap().is_changed());
939 assert!(q.get(&world, id4).unwrap().is_changed());
940
941 world.clear_trackers();
942 schedule.run(&mut world);
943
944 assert!(!q.get(&world, id1).unwrap().is_changed());
945 assert!(!q.get(&world, id2).unwrap().is_changed());
946 assert!(!q.get(&world, id3).unwrap().is_changed());
947 assert!(!q.get(&world, id4).unwrap().is_changed());
948 }
949
950 #[test]
951 fn visibility_propagation_with_invalid_parent() {
952 let mut world = World::new();
953 let mut schedule = Schedule::default();
954 schedule.add_systems(visibility_propagate_system);
955
956 let parent = world.spawn(()).id();
957 let child = world.spawn(Visibility::default()).id();
958 world.entity_mut(parent).add_children(&[child]);
959
960 schedule.run(&mut world);
961 world.clear_trackers();
962
963 let child_visible = world.entity(child).get::<InheritedVisibility>().unwrap().0;
964 assert!(child_visible);
966 }
967
968 #[test]
969 fn ensure_visibility_enum_size() {
970 assert_eq!(1, size_of::<Visibility>());
971 assert_eq!(1, size_of::<Option<Visibility>>());
972 }
973}