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}