Skip to main content

bevy_render/view/visibility/
mod.rs

1use core::{any::TypeId, mem};
2
3use bevy_ecs::{
4    change_detection::Tick,
5    component::Component,
6    entity::Entity,
7    prelude::ReflectComponent,
8    query::{Changed, Or, With},
9    system::{
10        lifetimeless::{Read, SQuery},
11        Local, Query, SystemParam,
12    },
13};
14#[cfg(feature = "trace")]
15use bevy_log::info_span;
16use bevy_platform::collections::{HashMap, HashSet};
17use bevy_reflect::{prelude::ReflectDefault, Reflect};
18use bevy_utils::TypeIdHashMap;
19
20use crate::{
21    sync_world::{MainEntity, MainEntityHashMap, RenderEntity},
22    view::RetainedViewEntity,
23    Extract,
24};
25
26mod range;
27use bevy_camera::visibility::*;
28pub use range::*;
29
30/// Stores a list of all entities that are visible from a single view or
31/// subview, as well as the change lists.
32///
33/// This component is only placed directly on camera entities. Lights instead
34/// have a [`RenderShadowMapVisibleEntities`] component that contains one or
35/// more [`RenderVisibleEntities`] components, one for each cascade or cubemap
36/// side.
37///
38/// The [`crate::camera::extract_cameras`] and `extract_lights` systems create
39/// this object, but they don't populate it. Instead, the
40/// [`collect_visible_cpu_culled_entities`] and
41/// `collect_gpu_culled_meshes` systems are responsible for
42/// updating this component from the lists of entities in
43/// [`RenderExtractedVisibleEntities`] and `RenderGpuCulledEntities`,
44/// respectively.
45#[derive(Clone, Component, Default, Debug)]
46pub struct RenderVisibleEntities {
47    /// Entities visible from this view or subview, sorted by
48    /// [`VisibilityClass`].
49    pub classes: TypeIdHashMap<RenderVisibleEntitiesClass>,
50}
51
52/// Collection of entities visible from a single light.
53///
54/// This component contains one [`RenderVisibleEntities`] object per subview.
55/// Directional lights have one subview per cascade, point lights have one
56/// subview per cubemap face, and spot lights only have a single subview.
57///
58/// The `extract_lights` system creates this component, but it doesn't populate
59/// it. Instead, the [`collect_visible_cpu_culled_entities`] and
60/// `collect_gpu_culled_meshes` systems are responsible for
61/// updating this component from the lists of entities in
62/// [`RenderExtractedShadowMapVisibleEntities`] and `RenderGpuCulledEntities`,
63/// respectively.
64#[derive(Clone, Component, Default, Debug, Reflect)]
65#[reflect(Component, Default, Debug, Clone)]
66pub struct RenderShadowMapVisibleEntities {
67    /// A mapping from each subview (cascade or cubemap face) to the entities
68    /// visible from it.
69    #[reflect(ignore, clone)]
70    pub subviews: HashMap<RetainedViewEntity, (Tick, RenderVisibleEntities)>,
71}
72
73/// Stores a list of all entities that are visible from a single view for a
74/// single [`VisibilityClass`], as well as the change lists.
75///
76/// Note that all lists in this component are guaranteed to be sorted. Thus you
77/// can test for the presence of an entity in these lists via binary search.
78///
79/// Note also that, for 3D meshes, the render-world [`Entity`] values will
80/// always be [`Entity::PLACEHOLDER`]. The render-world entities are kept for
81/// legacy systems that still need to process visibility of render-world
82/// entities.
83///
84/// The [`collect_visible_cpu_culled_entities`] and `collect_gpu_culled_meshes`
85/// systems populate this object from the corresponding
86/// [`RenderExtractedVisibleEntitiesClass`] object and the
87/// `RenderGpuCulledEntities` resource, respectively.
88#[derive(Clone, Debug, Default, Reflect)]
89#[reflect(Debug, Default, Clone)]
90pub struct RenderVisibleEntitiesClass {
91    /// A sorted list of all entities that don't have [`NoCpuCulling`]
92    /// components and are visible from this view.
93    #[reflect(ignore, clone)]
94    pub entities_cpu_culling: Vec<(Entity, MainEntity)>,
95
96    /// A table of all entities that have [`NoCpuCulling`] components and have
97    /// [`bevy_camera::visibility::InheritedVisibility`] set to true.
98    ///
99    /// The `collect_gpu_culled_meshes` system keeps this up to date.
100    pub entities_gpu_culling: MainEntityHashMap<Entity>,
101
102    /// A sorted list of all entities that were invisible last frame (including
103    /// ones that didn't exist at all last frame) and became visible this frame.
104    pub added_entities: Vec<(Entity, MainEntity)>,
105
106    /// A sorted list of all entities that were visible last frame and became
107    /// invisible this frame, including those that were despawned this frame.
108    pub removed_entities: Vec<(Entity, MainEntity)>,
109}
110
111/// The entities that the CPU has determined are visible from a single view or
112/// subview.
113///
114/// This component is only placed directly on camera entities. Lights instead
115/// have a [`RenderExtractedShadowMapVisibleEntities`] component that contains
116/// one or more [`RenderExtractedVisibleEntities`] components, one for each
117/// cascade or cubemap side.
118///
119/// Mesh entities with [`NoCpuCulling`] aren't present in this table. Instead,
120/// `collect_gpu_culled_meshes` fetches them directly from the
121/// `RenderGpuCulledEntities` list.
122///
123/// The [`crate::camera::extract_cameras`] and `extract_lights` systems populate
124/// this object, and the [`collect_visible_cpu_culled_entities`] system reads it.
125#[derive(Component, Clone, Default, Debug)]
126pub struct RenderExtractedVisibleEntities {
127    /// Entities that the CPU has determined to be visible from this view or
128    /// subview, sorted by [`VisibilityClass`].
129    pub classes: TypeIdHashMap<RenderExtractedVisibleEntitiesClass>,
130}
131
132/// The entities that the CPU has determined are visible from a single
133/// shadow-casting light.
134///
135/// This component contains one [`RenderExtractedVisibleEntities`] object per
136/// subview.  Directional lights have one subview per cascade, point lights have
137/// one subview per cubemap face, and spot lights only have a single subview.
138///
139/// Mesh entities that have [`NoCpuCulling`] components aren't in this list.
140/// Instead, `collect_gpu_culled_meshes` fetches them directly
141/// from the `RenderGpuCulledEntities` table.
142///
143/// The `extract_lights` system populates this component, and the
144/// [`collect_visible_cpu_culled_entities`] system reads it.
145#[derive(Component, Default)]
146pub struct RenderExtractedShadowMapVisibleEntities {
147    /// A mapping from the subview to the list of entities that the CPU has
148    /// determined are visible from it.
149    pub subviews: HashMap<RetainedViewEntity, RenderExtractedVisibleEntities>,
150}
151
152/// The entities that the CPU has determined are visible from a single view or
153/// subview, for a single [`VisibilityClass`].
154///
155/// Mesh entities that have [`NoCpuCulling`] components aren't in this list.
156/// Instead, `collect_gpu_culled_meshes` fetches them directly
157/// from the `RenderGpuCulledEntities` table in order to update the
158/// [`RenderVisibleEntitiesClass`].
159///
160/// The [`crate::camera::extract_cameras`] and `extract_lights` systems populate
161/// this object, and the [`collect_visible_cpu_culled_entities`] system reads it.
162#[derive(Clone, Default, Debug)]
163pub struct RenderExtractedVisibleEntitiesClass {
164    /// A sorted list of entities that don't have [`NoCpuCulling`] components
165    /// and are visible from this view or subview.
166    pub entities: Vec<(Entity, MainEntity)>,
167}
168
169impl RenderVisibleEntities {
170    /// Returns the [`RenderVisibleEntitiesClass`] corresponding to the given
171    /// [`VisibilityClass`].
172    pub fn get<QF>(&self) -> Option<&RenderVisibleEntitiesClass>
173    where
174        QF: 'static,
175    {
176        self.classes.get(&TypeId::of::<QF>())
177    }
178}
179
180impl RenderVisibleEntitiesClass {
181    /// Clears out the lists of added and removed entities in preparation for a
182    /// new frame.
183    pub fn prepare_for_new_frame(&mut self) {
184        self.added_entities.clear();
185        self.removed_entities.clear();
186    }
187
188    /// Processes a list of visible entities for a new frame, computing the set
189    /// of newly-added and newly-removed entities as it goes.
190    ///
191    /// This function only handles entities that are culled on CPU (i.e. don't
192    /// have `NoCpuCulling` components). Entities that use only GPU culling are
193    /// instead fetched from the main world and added to the
194    /// `RenderGpuCulledEntities` table.
195    pub fn update_cpu_culled_entities(
196        &mut self,
197        visible_mesh_entities_cpu_culling: &[(Entity, MainEntity)],
198    ) {
199        #[cfg(feature = "trace")]
200        let _update_from = info_span!("update_from", name = "update_from").entered();
201
202        let old_entities_cpu_culling = mem::take(&mut self.entities_cpu_culling);
203
204        // March over the old and new visible CPU culling entity lists in
205        // lockstep, diffing as we go to determine the added and removed
206        // entities. The lists must be sorted.
207        let mut old_entity_cpu_culling_iter = old_entities_cpu_culling.iter().peekable();
208        {
209            #[cfg(feature = "trace")]
210            let _old_entity_cpu_culling_span =
211                info_span!("old_entity_cpu_culling", name = "old_entity_cpu_culling").entered();
212            for (render_entity, visible_main_entity) in visible_mesh_entities_cpu_culling {
213                // Mark entities as removed until we see the one we're looking at.
214                while old_entity_cpu_culling_iter
215                    .peek()
216                    .is_some_and(|(_, main_entity)| *main_entity < *visible_main_entity)
217                {
218                    self.removed_entities
219                        .push(*old_entity_cpu_culling_iter.next().unwrap());
220                }
221
222                // Add the visible entity to the list.
223                self.entities_cpu_culling
224                    .push((*render_entity, *visible_main_entity));
225
226                // If the next entity in the old list isn't equal to the entity we
227                // just marked visible, then our entity is newly visible this frame.
228                if old_entity_cpu_culling_iter
229                    .peek()
230                    .is_some_and(|&&(_, main_entity)| main_entity == *visible_main_entity)
231                {
232                    old_entity_cpu_culling_iter.next();
233                } else {
234                    self.added_entities
235                        .push((*render_entity, *visible_main_entity));
236                }
237            }
238        }
239
240        // Any entities that do CPU culling and that we didn't see yet are
241        // removed, so drain them.
242        {
243            #[cfg(feature = "trace")]
244            let _old_entity_cpu_culling_removal_span = info_span!(
245                "old_entity_cpu_culling_removal",
246                name = "old_entity_cpu_culling_removal"
247            )
248            .entered();
249            self.removed_entities
250                .extend(old_entity_cpu_culling_iter.copied());
251        }
252    }
253
254    /// Returns the list of newly-added entities.
255    pub fn added_entities(&self) -> &[(Entity, MainEntity)] {
256        &self.added_entities
257    }
258
259    /// Returns true if the given entity pair is known to be visible.
260    ///
261    /// This checks both the CPU culling visible entries table and the
262    /// no-CPU-culling visible entries table.
263    pub fn entity_pair_is_visible(&self, entity: Entity, main_entity: MainEntity) -> bool {
264        self.entities_cpu_culling
265            // We need to search by the same key used to sort the vec
266            // in `collect_visible_cpu_culled_entities_for_subview()`
267            .binary_search_by_key(&main_entity, |(_, main_entity)| *main_entity)
268            .is_ok_and(|index| self.entities_cpu_culling[index].0 == entity)
269            || self
270                .entities_gpu_culling
271                .get(&main_entity)
272                .is_some_and(|that_entity| *that_entity == entity)
273    }
274
275    /// Iterates over all visible entities.
276    ///
277    /// This is an expensive operation, so try to avoid doing it unless
278    /// necessary (e.g. the view key changed).
279    pub fn iter_visible<'a>(&'a self) -> impl Iterator<Item = (&'a Entity, &'a MainEntity)> {
280        self.entities_cpu_culling
281            .iter()
282            .map(|(entity, main_entity)| (entity, main_entity))
283            .chain(
284                self.entities_gpu_culling
285                    .iter()
286                    .map(|(main_entity, entity)| (entity, main_entity)),
287            )
288    }
289
290    /// Sorts the [`Self::added_entities`] list.
291    ///
292    /// You must call this after pushing entities onto the list, as the
293    /// `DirtySpecializations` iterator will binary search it.
294    pub fn sort_added_entities(&mut self) {
295        self.added_entities
296            .sort_unstable_by_key(|(_, main_entity)| *main_entity);
297    }
298}
299
300/// A system parameter that goes on any render-world system that needs to
301/// extract entities into [`RenderVisibleEntities`].
302#[derive(SystemParam)]
303pub struct VisibilityExtractionSystemParam<'w, 's> {
304    /// Maps entities in the main world to entities in the render world.
305    pub mapper: Extract<'w, 's, SQuery<Read<RenderEntity>>>,
306}
307
308/// The query, part of [`VisibilityExtractionSystemParam`], that searches for
309/// entities with [`NoCpuCulling`] that might have changed visibility.
310pub type VisibilityExtractionNoCpuCullingChangedQuery = SQuery<
311    (Entity, Read<VisibilityClass>, Read<InheritedVisibility>),
312    (
313        Or<(Changed<NoCpuCulling>, Changed<InheritedVisibility>)>,
314        With<NoCpuCulling>,
315    ),
316>;
317
318/// Updates the [`RenderVisibleEntities`] and [`RenderShadowMapVisibleEntities`]
319/// components with the contents of the [`RenderExtractedVisibleEntities`] and
320/// the [`RenderExtractedShadowMapVisibleEntities`] components respectively.
321///
322/// This system only handles CPU-culled entities (i.e. those without
323/// [`NoCpuCulling`] components). The `collect_gpu_culled_meshes` system in
324/// `bevy_pbr` handles GPU-culled entities.
325pub fn collect_visible_cpu_culled_entities(
326    mut cameras: Query<(
327        &mut RenderVisibleEntities,
328        Option<&mut RenderExtractedVisibleEntities>,
329    )>,
330    mut lights: Query<(
331        &mut RenderShadowMapVisibleEntities,
332        Option<&mut RenderExtractedShadowMapVisibleEntities>,
333    )>,
334    mut visibility_classes: Local<HashSet<TypeId>>,
335) {
336    // Collect cameras.
337    for (mut render_visible_entities, mut maybe_render_visible_entities_cpu_culling) in
338        cameras.iter_mut()
339    {
340        let mut maybe_render_subview_visible_entities_cpu_culling =
341            maybe_render_visible_entities_cpu_culling.as_deref_mut();
342        collect_visible_cpu_culled_entities_for_subview(
343            &mut render_visible_entities,
344            &mut maybe_render_subview_visible_entities_cpu_culling,
345            &mut visibility_classes,
346        );
347    }
348
349    // Collect shadow maps.
350    for (
351        mut render_shadow_map_visible_entities,
352        mut maybe_render_shadow_map_visible_entities_cpu_culling,
353    ) in lights.iter_mut()
354    {
355        for (subview, (_, render_visible_entities)) in
356            render_shadow_map_visible_entities.subviews.iter_mut()
357        {
358            let mut maybe_render_subview_visible_entities_cpu_culling =
359                maybe_render_shadow_map_visible_entities_cpu_culling
360                    .as_mut()
361                    .and_then(|render_subview_visible_entities_cpu_culling| {
362                        render_subview_visible_entities_cpu_culling
363                            .subviews
364                            .get_mut(subview)
365                    });
366            collect_visible_cpu_culled_entities_for_subview(
367                render_visible_entities,
368                &mut maybe_render_subview_visible_entities_cpu_culling,
369                &mut visibility_classes,
370            );
371        }
372    }
373}
374
375/// Updates the [`RenderVisibleEntities`] list for a single subview from the
376/// applicable [`RenderExtractedVisibleEntities`].
377///
378/// This only handles CPU-culled entities. The corresponding function for
379/// GPU-called entities is `collect_gpu_culled_meshes_for_subview` in
380/// `bevy_pbr`.
381fn collect_visible_cpu_culled_entities_for_subview(
382    render_visible_entities: &mut RenderVisibleEntities,
383    maybe_render_subview_visible_entities: &mut Option<&mut RenderExtractedVisibleEntities>,
384    visibility_classes: &mut HashSet<TypeId>,
385) {
386    // Gather up all visibility classes. We need to make sure that the
387    // `RenderVisibleEntities` has an entry for each one.
388    visibility_classes.clear();
389    visibility_classes.extend(render_visible_entities.classes.keys().copied());
390    if let Some(ref mut render_subview_visible_entities) = *maybe_render_subview_visible_entities {
391        visibility_classes.extend(render_subview_visible_entities.classes.keys().copied());
392    }
393
394    // Update the tables of each visibility class.
395    for visibility_class in visibility_classes.iter() {
396        let entities = render_visible_entities
397            .classes
398            .entry(*visibility_class)
399            .or_default();
400
401        entities.prepare_for_new_frame();
402
403        // Fetch the visibility class's entity table.
404        let Some(ref mut render_subview_visible_entities) = *maybe_render_subview_visible_entities
405        else {
406            continue;
407        };
408        let Some(render_view_entities) = render_subview_visible_entities
409            .classes
410            .get_mut(visibility_class)
411        else {
412            continue;
413        };
414
415        // Make sure the entity list is sorted, as this is a requirement for
416        // [`RenderVisibleEntitiesClass::update_from_cpu`].
417        render_view_entities
418            .entities
419            .sort_unstable_by_key(|(_, main_entity)| *main_entity);
420
421        entities.update_cpu_culled_entities(&render_view_entities.entities);
422    }
423}
424
425#[cfg(test)]
426mod tests {
427    use super::*;
428
429    #[test]
430    fn cpu_visible_entity_pair_lookup_uses_main_entity_sort_order() {
431        struct TestVisibilityClass;
432
433        let main_a = MainEntity::from(Entity::from_bits(1));
434        let main_b = MainEntity::from(Entity::from_bits(2));
435
436        // We need the render entities to sort differently from the main entities
437        let render_a = Entity::from_bits(2);
438        let render_b = Entity::from_bits(1);
439
440        let mut pairs = vec![(render_a, main_a), (render_b, main_b)];
441
442        let mut extracted = RenderExtractedVisibleEntities {
443            classes: TypeIdHashMap::from_iter([(
444                TypeId::of::<TestVisibilityClass>(),
445                RenderExtractedVisibleEntitiesClass {
446                    entities: pairs.clone(),
447                },
448            )]),
449        };
450        let mut render_visible_entities = RenderVisibleEntities::default();
451
452        collect_visible_cpu_culled_entities_for_subview(
453            &mut render_visible_entities,
454            &mut Some(&mut extracted),
455            &mut HashSet::default(),
456        );
457
458        let visible = render_visible_entities
459            .get::<TestVisibilityClass>()
460            .unwrap();
461
462        // Sort the pairs by the full tuple and confirm that the sort for visible entities is
463        // different
464        pairs.sort();
465        assert_ne!(visible.entities_cpu_culling, pairs);
466        // Sort the pairs by only the main_entity to make sure the order is as expected
467        pairs.sort_by_key(|(_, main_entity)| *main_entity);
468        assert_eq!(visible.entities_cpu_culling, pairs);
469
470        // Make sure the visibility lookup works
471        for (entity, main_entity) in &pairs {
472            assert!(
473                visible.entity_pair_is_visible(*entity, *main_entity),
474                "expected {entity:?}/{main_entity:?} to be visible"
475            );
476        }
477    }
478}