Skip to main content

bevy_render/view/visibility/
mod.rs

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