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}