bevy_render/render_phase/mod.rs
1//! The modular rendering abstraction responsible for queuing, preparing, sorting and drawing
2//! entities as part of separate render phases.
3//!
4//! In Bevy each view (camera, or shadow-casting light, etc.) has one or multiple render phases
5//! (e.g. opaque, transparent, shadow, etc).
6//! They are used to queue entities for rendering.
7//! Multiple phases might be required due to different sorting/batching behaviors
8//! (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
9//! the rendered texture of the previous phase (e.g. for screen-space reflections).
10//!
11//! To draw an entity, a corresponding [`PhaseItem`] has to be added to one or multiple of these
12//! render phases for each view that it is visible in.
13//! This must be done in the [`RenderSet::Queue`].
14//! After that the render phase sorts them in the [`RenderSet::PhaseSort`].
15//! Finally the items are rendered using a single [`TrackedRenderPass`], during
16//! the [`RenderSet::Render`].
17//!
18//! Therefore each phase item is assigned a [`Draw`] function.
19//! These set up the state of the [`TrackedRenderPass`] (i.e. select the
20//! [`RenderPipeline`](crate::render_resource::RenderPipeline), configure the
21//! [`BindGroup`](crate::render_resource::BindGroup)s, etc.) and then issue a draw call,
22//! for the corresponding item.
23//!
24//! The [`Draw`] function trait can either be implemented directly or such a function can be
25//! created by composing multiple [`RenderCommand`]s.
26
27mod draw;
28mod draw_state;
29mod rangefinder;
30
31use bevy_app::{App, Plugin};
32use bevy_derive::{Deref, DerefMut};
33use bevy_ecs::component::Tick;
34use bevy_ecs::entity::EntityHash;
35use bevy_platform::collections::{hash_map::Entry, HashMap};
36use bevy_utils::default;
37pub use draw::*;
38pub use draw_state::*;
39use encase::{internal::WriteInto, ShaderSize};
40use fixedbitset::{Block, FixedBitSet};
41use indexmap::IndexMap;
42use nonmax::NonMaxU32;
43pub use rangefinder::*;
44use wgpu::Features;
45
46use crate::batching::gpu_preprocessing::{
47 GpuPreprocessingMode, GpuPreprocessingSupport, PhaseBatchedInstanceBuffers,
48 PhaseIndirectParametersBuffers,
49};
50use crate::renderer::RenderDevice;
51use crate::sync_world::{MainEntity, MainEntityHashMap};
52use crate::view::RetainedViewEntity;
53use crate::RenderDebugFlags;
54use crate::{
55 batching::{
56 self,
57 gpu_preprocessing::{self, BatchedInstanceBuffers},
58 no_gpu_preprocessing::{self, BatchedInstanceBuffer},
59 GetFullBatchData,
60 },
61 render_resource::{CachedRenderPipelineId, GpuArrayBufferIndex, PipelineCache},
62 Render, RenderApp, RenderSet,
63};
64use bevy_ecs::{
65 prelude::*,
66 system::{lifetimeless::SRes, SystemParamItem},
67};
68use core::{fmt::Debug, hash::Hash, iter, marker::PhantomData, ops::Range, slice::SliceIndex};
69use smallvec::SmallVec;
70use tracing::warn;
71
72/// Stores the rendering instructions for a single phase that uses bins in all
73/// views.
74///
75/// They're cleared out every frame, but storing them in a resource like this
76/// allows us to reuse allocations.
77#[derive(Resource, Deref, DerefMut)]
78pub struct ViewBinnedRenderPhases<BPI>(pub HashMap<RetainedViewEntity, BinnedRenderPhase<BPI>>)
79where
80 BPI: BinnedPhaseItem;
81
82/// A collection of all rendering instructions, that will be executed by the GPU, for a
83/// single render phase for a single view.
84///
85/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
86/// They are used to queue entities for rendering.
87/// Multiple phases might be required due to different sorting/batching behaviors
88/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
89/// the rendered texture of the previous phase (e.g. for screen-space reflections).
90/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
91/// The render pass might be reused for multiple phases to reduce GPU overhead.
92///
93/// This flavor of render phase is used for phases in which the ordering is less
94/// critical: for example, `Opaque3d`. It's generally faster than the
95/// alternative [`SortedRenderPhase`].
96pub struct BinnedRenderPhase<BPI>
97where
98 BPI: BinnedPhaseItem,
99{
100 /// The multidrawable bins.
101 ///
102 /// Each batch set key maps to a *batch set*, which in this case is a set of
103 /// meshes that can be drawn together in one multidraw call. Each batch set
104 /// is subdivided into *bins*, each of which represents a particular mesh.
105 /// Each bin contains the entity IDs of instances of that mesh.
106 ///
107 /// So, for example, if there are two cubes and a sphere present in the
108 /// scene, we would generally have one batch set containing two bins,
109 /// assuming that the cubes and sphere meshes are allocated together and use
110 /// the same pipeline. The first bin, corresponding to the cubes, will have
111 /// two entities in it. The second bin, corresponding to the sphere, will
112 /// have one entity in it.
113 pub multidrawable_meshes: IndexMap<BPI::BatchSetKey, IndexMap<BPI::BinKey, RenderBin>>,
114
115 /// The bins corresponding to batchable items that aren't multidrawable.
116 ///
117 /// For multidrawable entities, use `multidrawable_meshes`; for
118 /// unbatchable entities, use `unbatchable_values`.
119 pub batchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
120
121 /// The unbatchable bins.
122 ///
123 /// Each entity here is rendered in a separate drawcall.
124 pub unbatchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
125
126 /// Items in the bin that aren't meshes at all.
127 ///
128 /// Bevy itself doesn't place anything in this list, but plugins or your app
129 /// can in order to execute custom drawing commands. Draw functions for each
130 /// entity are simply called in order at rendering time.
131 ///
132 /// See the `custom_phase_item` example for an example of how to use this.
133 pub non_mesh_items: IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
134
135 /// Information on each batch set.
136 ///
137 /// A *batch set* is a set of entities that will be batched together unless
138 /// we're on a platform that doesn't support storage buffers (e.g. WebGL 2)
139 /// and differing dynamic uniform indices force us to break batches. On
140 /// platforms that support storage buffers, a batch set always consists of
141 /// at most one batch.
142 ///
143 /// Multidrawable entities come first, then batchable entities, then
144 /// unbatchable entities.
145 pub(crate) batch_sets: BinnedRenderPhaseBatchSets<BPI::BinKey>,
146
147 /// The batch and bin key for each entity.
148 ///
149 /// We retain these so that, when the entity changes,
150 /// [`Self::sweep_old_entities`] can quickly find the bin it was located in
151 /// and remove it.
152 cached_entity_bin_keys: IndexMap<MainEntity, CachedBinnedEntity<BPI>, EntityHash>,
153
154 /// The set of indices in [`Self::cached_entity_bin_keys`] that are
155 /// confirmed to be up to date.
156 ///
157 /// Note that each bit in this bit set refers to an *index* in the
158 /// [`IndexMap`] (i.e. a bucket in the hash table). They aren't entity IDs.
159 valid_cached_entity_bin_keys: FixedBitSet,
160
161 /// The set of entities that changed bins this frame.
162 ///
163 /// An entity will only be present in this list if it was in one bin on the
164 /// previous frame and is in a new bin on this frame. Each list entry
165 /// specifies the bin the entity used to be in. We use this in order to
166 /// remove the entity from the old bin during
167 /// [`BinnedRenderPhase::sweep_old_entities`].
168 entities_that_changed_bins: Vec<EntityThatChangedBins<BPI>>,
169 /// The gpu preprocessing mode configured for the view this phase is associated
170 /// with.
171 gpu_preprocessing_mode: GpuPreprocessingMode,
172}
173
174/// All entities that share a mesh and a material and can be batched as part of
175/// a [`BinnedRenderPhase`].
176#[derive(Default)]
177pub struct RenderBin {
178 /// A list of the entities in each bin, along with their cached
179 /// [`InputUniformIndex`].
180 entities: IndexMap<MainEntity, InputUniformIndex, EntityHash>,
181}
182
183/// Information that we track about an entity that was in one bin on the
184/// previous frame and is in a different bin this frame.
185struct EntityThatChangedBins<BPI>
186where
187 BPI: BinnedPhaseItem,
188{
189 /// The entity.
190 main_entity: MainEntity,
191 /// The key that identifies the bin that this entity used to be in.
192 old_cached_binned_entity: CachedBinnedEntity<BPI>,
193}
194
195/// Information that we keep about an entity currently within a bin.
196pub struct CachedBinnedEntity<BPI>
197where
198 BPI: BinnedPhaseItem,
199{
200 /// Information that we use to identify a cached entity in a bin.
201 pub cached_bin_key: Option<CachedBinKey<BPI>>,
202 /// The last modified tick of the entity.
203 ///
204 /// We use this to detect when the entity needs to be invalidated.
205 pub change_tick: Tick,
206}
207
208/// Information that we use to identify a cached entity in a bin.
209pub struct CachedBinKey<BPI>
210where
211 BPI: BinnedPhaseItem,
212{
213 /// The key of the batch set containing the entity.
214 pub batch_set_key: BPI::BatchSetKey,
215 /// The key of the bin containing the entity.
216 pub bin_key: BPI::BinKey,
217 /// The type of render phase that we use to render the entity: multidraw,
218 /// plain batch, etc.
219 pub phase_type: BinnedRenderPhaseType,
220}
221
222impl<BPI> Clone for CachedBinnedEntity<BPI>
223where
224 BPI: BinnedPhaseItem,
225{
226 fn clone(&self) -> Self {
227 CachedBinnedEntity {
228 cached_bin_key: self.cached_bin_key.clone(),
229 change_tick: self.change_tick,
230 }
231 }
232}
233
234impl<BPI> Clone for CachedBinKey<BPI>
235where
236 BPI: BinnedPhaseItem,
237{
238 fn clone(&self) -> Self {
239 CachedBinKey {
240 batch_set_key: self.batch_set_key.clone(),
241 bin_key: self.bin_key.clone(),
242 phase_type: self.phase_type,
243 }
244 }
245}
246
247impl<BPI> PartialEq for CachedBinKey<BPI>
248where
249 BPI: BinnedPhaseItem,
250{
251 fn eq(&self, other: &Self) -> bool {
252 self.batch_set_key == other.batch_set_key
253 && self.bin_key == other.bin_key
254 && self.phase_type == other.phase_type
255 }
256}
257
258/// How we store and render the batch sets.
259///
260/// Each one of these corresponds to a [`GpuPreprocessingMode`].
261pub enum BinnedRenderPhaseBatchSets<BK> {
262 /// Batches are grouped into batch sets based on dynamic uniforms.
263 ///
264 /// This corresponds to [`GpuPreprocessingMode::None`].
265 DynamicUniforms(Vec<SmallVec<[BinnedRenderPhaseBatch; 1]>>),
266
267 /// Batches are never grouped into batch sets.
268 ///
269 /// This corresponds to [`GpuPreprocessingMode::PreprocessingOnly`].
270 Direct(Vec<BinnedRenderPhaseBatch>),
271
272 /// Batches are grouped together into batch sets based on their ability to
273 /// be multi-drawn together.
274 ///
275 /// This corresponds to [`GpuPreprocessingMode::Culling`].
276 MultidrawIndirect(Vec<BinnedRenderPhaseBatchSet<BK>>),
277}
278
279/// A group of entities that will be batched together into a single multi-draw
280/// call.
281pub struct BinnedRenderPhaseBatchSet<BK> {
282 /// The first batch in this batch set.
283 pub(crate) first_batch: BinnedRenderPhaseBatch,
284 /// The key of the bin that the first batch corresponds to.
285 pub(crate) bin_key: BK,
286 /// The number of batches.
287 pub(crate) batch_count: u32,
288 /// The index of the batch set in the GPU buffer.
289 pub(crate) index: u32,
290}
291
292impl<BK> BinnedRenderPhaseBatchSets<BK> {
293 fn clear(&mut self) {
294 match *self {
295 BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut vec) => vec.clear(),
296 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => vec.clear(),
297 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => vec.clear(),
298 }
299 }
300}
301
302/// Information about a single batch of entities rendered using binned phase
303/// items.
304#[derive(Debug)]
305pub struct BinnedRenderPhaseBatch {
306 /// An entity that's *representative* of this batch.
307 ///
308 /// Bevy uses this to fetch the mesh. It can be any entity in the batch.
309 pub representative_entity: (Entity, MainEntity),
310 /// The range of instance indices in this batch.
311 pub instance_range: Range<u32>,
312
313 /// The dynamic offset of the batch.
314 ///
315 /// Note that dynamic offsets are only used on platforms that don't support
316 /// storage buffers.
317 pub extra_index: PhaseItemExtraIndex,
318}
319
320/// Information about the unbatchable entities in a bin.
321pub struct UnbatchableBinnedEntities {
322 /// The entities.
323 pub entities: MainEntityHashMap<Entity>,
324
325 /// The GPU array buffer indices of each unbatchable binned entity.
326 pub(crate) buffer_indices: UnbatchableBinnedEntityIndexSet,
327}
328
329/// Information about [`BinnedRenderPhaseType::NonMesh`] entities.
330pub struct NonMeshEntities {
331 /// The entities.
332 pub entities: MainEntityHashMap<Entity>,
333}
334
335/// Stores instance indices and dynamic offsets for unbatchable entities in a
336/// binned render phase.
337///
338/// This is conceptually `Vec<UnbatchableBinnedEntityDynamicOffset>`, but it
339/// avoids the overhead of storing dynamic offsets on platforms that support
340/// them. In other words, this allows a fast path that avoids allocation on
341/// platforms that aren't WebGL 2.
342#[derive(Default)]
343
344pub(crate) enum UnbatchableBinnedEntityIndexSet {
345 /// There are no unbatchable entities in this bin (yet).
346 #[default]
347 NoEntities,
348
349 /// The instances for all unbatchable entities in this bin are contiguous,
350 /// and there are no dynamic uniforms.
351 ///
352 /// This is the typical case on platforms other than WebGL 2. We special
353 /// case this to avoid allocation on those platforms.
354 Sparse {
355 /// The range of indices.
356 instance_range: Range<u32>,
357 /// The index of the first indirect instance parameters.
358 ///
359 /// The other indices immediately follow these.
360 first_indirect_parameters_index: Option<NonMaxU32>,
361 },
362
363 /// Dynamic uniforms are present for unbatchable entities in this bin.
364 ///
365 /// We fall back to this on WebGL 2.
366 Dense(Vec<UnbatchableBinnedEntityIndices>),
367}
368
369/// The instance index and dynamic offset (if present) for an unbatchable entity.
370///
371/// This is only useful on platforms that don't support storage buffers.
372#[derive(Clone)]
373pub(crate) struct UnbatchableBinnedEntityIndices {
374 /// The instance index.
375 pub(crate) instance_index: u32,
376 /// The [`PhaseItemExtraIndex`], if present.
377 pub(crate) extra_index: PhaseItemExtraIndex,
378}
379
380/// Identifies the list within [`BinnedRenderPhase`] that a phase item is to be
381/// placed in.
382#[derive(Clone, Copy, PartialEq, Debug)]
383pub enum BinnedRenderPhaseType {
384 /// The item is a mesh that's eligible for multi-draw indirect rendering and
385 /// can be batched with other meshes of the same type.
386 MultidrawableMesh,
387
388 /// The item is a mesh that can be batched with other meshes of the same type and
389 /// drawn in a single draw call.
390 BatchableMesh,
391
392 /// The item is a mesh that's eligible for indirect rendering, but can't be
393 /// batched with other meshes of the same type.
394 UnbatchableMesh,
395
396 /// The item isn't a mesh at all.
397 ///
398 /// Bevy will simply invoke the drawing commands for such items one after
399 /// another, with no further processing.
400 ///
401 /// The engine itself doesn't enqueue any items of this type, but it's
402 /// available for use in your application and/or plugins.
403 NonMesh,
404}
405
406impl<T> From<GpuArrayBufferIndex<T>> for UnbatchableBinnedEntityIndices
407where
408 T: Clone + ShaderSize + WriteInto,
409{
410 fn from(value: GpuArrayBufferIndex<T>) -> Self {
411 UnbatchableBinnedEntityIndices {
412 instance_index: value.index,
413 extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(value.dynamic_offset),
414 }
415 }
416}
417
418impl<BPI> Default for ViewBinnedRenderPhases<BPI>
419where
420 BPI: BinnedPhaseItem,
421{
422 fn default() -> Self {
423 Self(default())
424 }
425}
426
427impl<BPI> ViewBinnedRenderPhases<BPI>
428where
429 BPI: BinnedPhaseItem,
430{
431 pub fn prepare_for_new_frame(
432 &mut self,
433 retained_view_entity: RetainedViewEntity,
434 gpu_preprocessing: GpuPreprocessingMode,
435 ) {
436 match self.entry(retained_view_entity) {
437 Entry::Occupied(mut entry) => entry.get_mut().prepare_for_new_frame(),
438 Entry::Vacant(entry) => {
439 entry.insert(BinnedRenderPhase::<BPI>::new(gpu_preprocessing));
440 }
441 }
442 }
443}
444
445/// The index of the uniform describing this object in the GPU buffer, when GPU
446/// preprocessing is enabled.
447///
448/// For example, for 3D meshes, this is the index of the `MeshInputUniform` in
449/// the buffer.
450///
451/// This field is ignored if GPU preprocessing isn't in use, such as (currently)
452/// in the case of 2D meshes. In that case, it can be safely set to
453/// [`core::default::Default::default`].
454#[derive(Clone, Copy, PartialEq, Default, Deref, DerefMut)]
455#[repr(transparent)]
456pub struct InputUniformIndex(pub u32);
457
458impl<BPI> BinnedRenderPhase<BPI>
459where
460 BPI: BinnedPhaseItem,
461{
462 /// Bins a new entity.
463 ///
464 /// The `phase_type` parameter specifies whether the entity is a
465 /// preprocessable mesh and whether it can be binned with meshes of the same
466 /// type.
467 pub fn add(
468 &mut self,
469 batch_set_key: BPI::BatchSetKey,
470 bin_key: BPI::BinKey,
471 (entity, main_entity): (Entity, MainEntity),
472 input_uniform_index: InputUniformIndex,
473 mut phase_type: BinnedRenderPhaseType,
474 change_tick: Tick,
475 ) {
476 // If the user has overridden indirect drawing for this view, we need to
477 // force the phase type to be batchable instead.
478 if self.gpu_preprocessing_mode == GpuPreprocessingMode::PreprocessingOnly
479 && phase_type == BinnedRenderPhaseType::MultidrawableMesh
480 {
481 phase_type = BinnedRenderPhaseType::BatchableMesh;
482 }
483
484 match phase_type {
485 BinnedRenderPhaseType::MultidrawableMesh => {
486 match self.multidrawable_meshes.entry(batch_set_key.clone()) {
487 indexmap::map::Entry::Occupied(mut entry) => {
488 entry
489 .get_mut()
490 .entry(bin_key.clone())
491 .or_default()
492 .insert(main_entity, input_uniform_index);
493 }
494 indexmap::map::Entry::Vacant(entry) => {
495 let mut new_batch_set = IndexMap::default();
496 new_batch_set.insert(
497 bin_key.clone(),
498 RenderBin::from_entity(main_entity, input_uniform_index),
499 );
500 entry.insert(new_batch_set);
501 }
502 }
503 }
504
505 BinnedRenderPhaseType::BatchableMesh => {
506 match self
507 .batchable_meshes
508 .entry((batch_set_key.clone(), bin_key.clone()).clone())
509 {
510 indexmap::map::Entry::Occupied(mut entry) => {
511 entry.get_mut().insert(main_entity, input_uniform_index);
512 }
513 indexmap::map::Entry::Vacant(entry) => {
514 entry.insert(RenderBin::from_entity(main_entity, input_uniform_index));
515 }
516 }
517 }
518
519 BinnedRenderPhaseType::UnbatchableMesh => {
520 match self
521 .unbatchable_meshes
522 .entry((batch_set_key.clone(), bin_key.clone()))
523 {
524 indexmap::map::Entry::Occupied(mut entry) => {
525 entry.get_mut().entities.insert(main_entity, entity);
526 }
527 indexmap::map::Entry::Vacant(entry) => {
528 let mut entities = MainEntityHashMap::default();
529 entities.insert(main_entity, entity);
530 entry.insert(UnbatchableBinnedEntities {
531 entities,
532 buffer_indices: default(),
533 });
534 }
535 }
536 }
537
538 BinnedRenderPhaseType::NonMesh => {
539 // We don't process these items further.
540 match self
541 .non_mesh_items
542 .entry((batch_set_key.clone(), bin_key.clone()).clone())
543 {
544 indexmap::map::Entry::Occupied(mut entry) => {
545 entry.get_mut().entities.insert(main_entity, entity);
546 }
547 indexmap::map::Entry::Vacant(entry) => {
548 let mut entities = MainEntityHashMap::default();
549 entities.insert(main_entity, entity);
550 entry.insert(NonMeshEntities { entities });
551 }
552 }
553 }
554 }
555
556 // Update the cache.
557 self.update_cache(
558 main_entity,
559 Some(CachedBinKey {
560 batch_set_key,
561 bin_key,
562 phase_type,
563 }),
564 change_tick,
565 );
566 }
567
568 /// Inserts an entity into the cache with the given change tick.
569 pub fn update_cache(
570 &mut self,
571 main_entity: MainEntity,
572 cached_bin_key: Option<CachedBinKey<BPI>>,
573 change_tick: Tick,
574 ) {
575 let new_cached_binned_entity = CachedBinnedEntity {
576 cached_bin_key,
577 change_tick,
578 };
579
580 let (index, old_cached_binned_entity) = self
581 .cached_entity_bin_keys
582 .insert_full(main_entity, new_cached_binned_entity.clone());
583
584 // If the entity changed bins, record its old bin so that we can remove
585 // the entity from it.
586 if let Some(old_cached_binned_entity) = old_cached_binned_entity {
587 if old_cached_binned_entity.cached_bin_key != new_cached_binned_entity.cached_bin_key {
588 self.entities_that_changed_bins.push(EntityThatChangedBins {
589 main_entity,
590 old_cached_binned_entity,
591 });
592 }
593 }
594
595 // Mark the entity as valid.
596 self.valid_cached_entity_bin_keys.grow_and_insert(index);
597 }
598
599 /// Encodes the GPU commands needed to render all entities in this phase.
600 pub fn render<'w>(
601 &self,
602 render_pass: &mut TrackedRenderPass<'w>,
603 world: &'w World,
604 view: Entity,
605 ) -> Result<(), DrawError> {
606 {
607 let draw_functions = world.resource::<DrawFunctions<BPI>>();
608 let mut draw_functions = draw_functions.write();
609 draw_functions.prepare(world);
610 // Make sure to drop the reader-writer lock here to avoid recursive
611 // locks.
612 }
613
614 self.render_batchable_meshes(render_pass, world, view)?;
615 self.render_unbatchable_meshes(render_pass, world, view)?;
616 self.render_non_meshes(render_pass, world, view)?;
617
618 Ok(())
619 }
620
621 /// Renders all batchable meshes queued in this phase.
622 fn render_batchable_meshes<'w>(
623 &self,
624 render_pass: &mut TrackedRenderPass<'w>,
625 world: &'w World,
626 view: Entity,
627 ) -> Result<(), DrawError> {
628 let draw_functions = world.resource::<DrawFunctions<BPI>>();
629 let mut draw_functions = draw_functions.write();
630
631 let render_device = world.resource::<RenderDevice>();
632 let multi_draw_indirect_count_supported = render_device
633 .features()
634 .contains(Features::MULTI_DRAW_INDIRECT_COUNT);
635
636 match self.batch_sets {
637 BinnedRenderPhaseBatchSets::DynamicUniforms(ref batch_sets) => {
638 debug_assert_eq!(self.batchable_meshes.len(), batch_sets.len());
639
640 for ((batch_set_key, bin_key), batch_set) in
641 self.batchable_meshes.keys().zip(batch_sets.iter())
642 {
643 for batch in batch_set {
644 let binned_phase_item = BPI::new(
645 batch_set_key.clone(),
646 bin_key.clone(),
647 batch.representative_entity,
648 batch.instance_range.clone(),
649 batch.extra_index.clone(),
650 );
651
652 // Fetch the draw function.
653 let Some(draw_function) =
654 draw_functions.get_mut(binned_phase_item.draw_function())
655 else {
656 continue;
657 };
658
659 draw_function.draw(world, render_pass, view, &binned_phase_item)?;
660 }
661 }
662 }
663
664 BinnedRenderPhaseBatchSets::Direct(ref batch_set) => {
665 for (batch, (batch_set_key, bin_key)) in
666 batch_set.iter().zip(self.batchable_meshes.keys())
667 {
668 let binned_phase_item = BPI::new(
669 batch_set_key.clone(),
670 bin_key.clone(),
671 batch.representative_entity,
672 batch.instance_range.clone(),
673 batch.extra_index.clone(),
674 );
675
676 // Fetch the draw function.
677 let Some(draw_function) =
678 draw_functions.get_mut(binned_phase_item.draw_function())
679 else {
680 continue;
681 };
682
683 draw_function.draw(world, render_pass, view, &binned_phase_item)?;
684 }
685 }
686
687 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) => {
688 for (batch_set_key, batch_set) in self
689 .multidrawable_meshes
690 .keys()
691 .chain(
692 self.batchable_meshes
693 .keys()
694 .map(|(batch_set_key, _)| batch_set_key),
695 )
696 .zip(batch_sets.iter())
697 {
698 let batch = &batch_set.first_batch;
699
700 let batch_set_index = if multi_draw_indirect_count_supported {
701 NonMaxU32::new(batch_set.index)
702 } else {
703 None
704 };
705
706 let binned_phase_item = BPI::new(
707 batch_set_key.clone(),
708 batch_set.bin_key.clone(),
709 batch.representative_entity,
710 batch.instance_range.clone(),
711 match batch.extra_index {
712 PhaseItemExtraIndex::None => PhaseItemExtraIndex::None,
713 PhaseItemExtraIndex::DynamicOffset(ref dynamic_offset) => {
714 PhaseItemExtraIndex::DynamicOffset(*dynamic_offset)
715 }
716 PhaseItemExtraIndex::IndirectParametersIndex { ref range, .. } => {
717 PhaseItemExtraIndex::IndirectParametersIndex {
718 range: range.start..(range.start + batch_set.batch_count),
719 batch_set_index,
720 }
721 }
722 },
723 );
724
725 // Fetch the draw function.
726 let Some(draw_function) =
727 draw_functions.get_mut(binned_phase_item.draw_function())
728 else {
729 continue;
730 };
731
732 draw_function.draw(world, render_pass, view, &binned_phase_item)?;
733 }
734 }
735 }
736
737 Ok(())
738 }
739
740 /// Renders all unbatchable meshes queued in this phase.
741 fn render_unbatchable_meshes<'w>(
742 &self,
743 render_pass: &mut TrackedRenderPass<'w>,
744 world: &'w World,
745 view: Entity,
746 ) -> Result<(), DrawError> {
747 let draw_functions = world.resource::<DrawFunctions<BPI>>();
748 let mut draw_functions = draw_functions.write();
749
750 for (batch_set_key, bin_key) in self.unbatchable_meshes.keys() {
751 let unbatchable_entities =
752 &self.unbatchable_meshes[&(batch_set_key.clone(), bin_key.clone())];
753 for (entity_index, entity) in unbatchable_entities.entities.iter().enumerate() {
754 let unbatchable_dynamic_offset = match &unbatchable_entities.buffer_indices {
755 UnbatchableBinnedEntityIndexSet::NoEntities => {
756 // Shouldn't happen…
757 continue;
758 }
759 UnbatchableBinnedEntityIndexSet::Sparse {
760 instance_range,
761 first_indirect_parameters_index,
762 } => UnbatchableBinnedEntityIndices {
763 instance_index: instance_range.start + entity_index as u32,
764 extra_index: match first_indirect_parameters_index {
765 None => PhaseItemExtraIndex::None,
766 Some(first_indirect_parameters_index) => {
767 let first_indirect_parameters_index_for_entity =
768 u32::from(*first_indirect_parameters_index)
769 + entity_index as u32;
770 PhaseItemExtraIndex::IndirectParametersIndex {
771 range: first_indirect_parameters_index_for_entity
772 ..(first_indirect_parameters_index_for_entity + 1),
773 batch_set_index: None,
774 }
775 }
776 },
777 },
778 UnbatchableBinnedEntityIndexSet::Dense(dynamic_offsets) => {
779 dynamic_offsets[entity_index].clone()
780 }
781 };
782
783 let binned_phase_item = BPI::new(
784 batch_set_key.clone(),
785 bin_key.clone(),
786 (*entity.1, *entity.0),
787 unbatchable_dynamic_offset.instance_index
788 ..(unbatchable_dynamic_offset.instance_index + 1),
789 unbatchable_dynamic_offset.extra_index,
790 );
791
792 // Fetch the draw function.
793 let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
794 else {
795 continue;
796 };
797
798 draw_function.draw(world, render_pass, view, &binned_phase_item)?;
799 }
800 }
801 Ok(())
802 }
803
804 /// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`].
805 ///
806 /// These will have been added by plugins or the application.
807 fn render_non_meshes<'w>(
808 &self,
809 render_pass: &mut TrackedRenderPass<'w>,
810 world: &'w World,
811 view: Entity,
812 ) -> Result<(), DrawError> {
813 let draw_functions = world.resource::<DrawFunctions<BPI>>();
814 let mut draw_functions = draw_functions.write();
815
816 for ((batch_set_key, bin_key), non_mesh_entities) in &self.non_mesh_items {
817 for (main_entity, entity) in non_mesh_entities.entities.iter() {
818 // Come up with a fake batch range and extra index. The draw
819 // function is expected to manage any sort of batching logic itself.
820 let binned_phase_item = BPI::new(
821 batch_set_key.clone(),
822 bin_key.clone(),
823 (*entity, *main_entity),
824 0..1,
825 PhaseItemExtraIndex::None,
826 );
827
828 let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
829 else {
830 continue;
831 };
832
833 draw_function.draw(world, render_pass, view, &binned_phase_item)?;
834 }
835 }
836
837 Ok(())
838 }
839
840 pub fn is_empty(&self) -> bool {
841 self.multidrawable_meshes.is_empty()
842 && self.batchable_meshes.is_empty()
843 && self.unbatchable_meshes.is_empty()
844 && self.non_mesh_items.is_empty()
845 }
846
847 pub fn prepare_for_new_frame(&mut self) {
848 self.batch_sets.clear();
849
850 self.valid_cached_entity_bin_keys.clear();
851 self.valid_cached_entity_bin_keys
852 .grow(self.cached_entity_bin_keys.len());
853 self.valid_cached_entity_bin_keys
854 .set_range(self.cached_entity_bin_keys.len().., true);
855
856 self.entities_that_changed_bins.clear();
857
858 for unbatchable_bin in self.unbatchable_meshes.values_mut() {
859 unbatchable_bin.buffer_indices.clear();
860 }
861 }
862
863 /// Checks to see whether the entity is in a bin and returns true if it's
864 /// both in a bin and up to date.
865 ///
866 /// If this function returns true, we also add the entry to the
867 /// `valid_cached_entity_bin_keys` list.
868 pub fn validate_cached_entity(
869 &mut self,
870 visible_entity: MainEntity,
871 current_change_tick: Tick,
872 ) -> bool {
873 if let indexmap::map::Entry::Occupied(entry) =
874 self.cached_entity_bin_keys.entry(visible_entity)
875 {
876 if entry.get().change_tick == current_change_tick {
877 self.valid_cached_entity_bin_keys.insert(entry.index());
878 return true;
879 }
880 }
881
882 false
883 }
884
885 /// Removes all entities not marked as clean from the bins.
886 ///
887 /// During `queue_material_meshes`, we process all visible entities and mark
888 /// each as clean as we come to it. Then, in [`sweep_old_entities`], we call
889 /// this method, which removes entities that aren't marked as clean from the
890 /// bins.
891 pub fn sweep_old_entities(&mut self) {
892 // Search for entities not marked as valid. We have to do this in
893 // reverse order because `swap_remove_index` will potentially invalidate
894 // all indices after the one we remove.
895 for index in ReverseFixedBitSetZeroesIterator::new(&self.valid_cached_entity_bin_keys) {
896 let Some((entity, cached_binned_entity)) =
897 self.cached_entity_bin_keys.swap_remove_index(index)
898 else {
899 continue;
900 };
901
902 if let Some(ref cached_bin_key) = cached_binned_entity.cached_bin_key {
903 remove_entity_from_bin(
904 entity,
905 cached_bin_key,
906 &mut self.multidrawable_meshes,
907 &mut self.batchable_meshes,
908 &mut self.unbatchable_meshes,
909 &mut self.non_mesh_items,
910 );
911 }
912 }
913
914 // If an entity changed bins, we need to remove it from its old bin.
915 for entity_that_changed_bins in self.entities_that_changed_bins.drain(..) {
916 let Some(ref old_cached_bin_key) = entity_that_changed_bins
917 .old_cached_binned_entity
918 .cached_bin_key
919 else {
920 continue;
921 };
922 remove_entity_from_bin(
923 entity_that_changed_bins.main_entity,
924 old_cached_bin_key,
925 &mut self.multidrawable_meshes,
926 &mut self.batchable_meshes,
927 &mut self.unbatchable_meshes,
928 &mut self.non_mesh_items,
929 );
930 }
931 }
932}
933
934/// Removes an entity from a bin.
935///
936/// If this makes the bin empty, this function removes the bin as well.
937///
938/// This is a standalone function instead of a method on [`BinnedRenderPhase`]
939/// for borrow check reasons.
940fn remove_entity_from_bin<BPI>(
941 entity: MainEntity,
942 entity_bin_key: &CachedBinKey<BPI>,
943 multidrawable_meshes: &mut IndexMap<BPI::BatchSetKey, IndexMap<BPI::BinKey, RenderBin>>,
944 batchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
945 unbatchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
946 non_mesh_items: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
947) where
948 BPI: BinnedPhaseItem,
949{
950 match entity_bin_key.phase_type {
951 BinnedRenderPhaseType::MultidrawableMesh => {
952 if let indexmap::map::Entry::Occupied(mut batch_set_entry) =
953 multidrawable_meshes.entry(entity_bin_key.batch_set_key.clone())
954 {
955 if let indexmap::map::Entry::Occupied(mut bin_entry) = batch_set_entry
956 .get_mut()
957 .entry(entity_bin_key.bin_key.clone())
958 {
959 bin_entry.get_mut().remove(entity);
960
961 // If the bin is now empty, remove the bin.
962 if bin_entry.get_mut().is_empty() {
963 bin_entry.swap_remove();
964 }
965 }
966
967 // If the batch set is now empty, remove it. This will perturb
968 // the order, but that's OK because we're going to sort the bin
969 // afterwards.
970 if batch_set_entry.get_mut().is_empty() {
971 batch_set_entry.swap_remove();
972 }
973 }
974 }
975
976 BinnedRenderPhaseType::BatchableMesh => {
977 if let indexmap::map::Entry::Occupied(mut bin_entry) = batchable_meshes.entry((
978 entity_bin_key.batch_set_key.clone(),
979 entity_bin_key.bin_key.clone(),
980 )) {
981 bin_entry.get_mut().remove(entity);
982
983 // If the bin is now empty, remove the bin.
984 if bin_entry.get_mut().is_empty() {
985 bin_entry.swap_remove();
986 }
987 }
988 }
989
990 BinnedRenderPhaseType::UnbatchableMesh => {
991 if let indexmap::map::Entry::Occupied(mut bin_entry) = unbatchable_meshes.entry((
992 entity_bin_key.batch_set_key.clone(),
993 entity_bin_key.bin_key.clone(),
994 )) {
995 bin_entry.get_mut().entities.remove(&entity);
996
997 // If the bin is now empty, remove the bin.
998 if bin_entry.get_mut().entities.is_empty() {
999 bin_entry.swap_remove();
1000 }
1001 }
1002 }
1003
1004 BinnedRenderPhaseType::NonMesh => {
1005 if let indexmap::map::Entry::Occupied(mut bin_entry) = non_mesh_items.entry((
1006 entity_bin_key.batch_set_key.clone(),
1007 entity_bin_key.bin_key.clone(),
1008 )) {
1009 bin_entry.get_mut().entities.remove(&entity);
1010
1011 // If the bin is now empty, remove the bin.
1012 if bin_entry.get_mut().entities.is_empty() {
1013 bin_entry.swap_remove();
1014 }
1015 }
1016 }
1017 }
1018}
1019
1020impl<BPI> BinnedRenderPhase<BPI>
1021where
1022 BPI: BinnedPhaseItem,
1023{
1024 fn new(gpu_preprocessing: GpuPreprocessingMode) -> Self {
1025 Self {
1026 multidrawable_meshes: IndexMap::default(),
1027 batchable_meshes: IndexMap::default(),
1028 unbatchable_meshes: IndexMap::default(),
1029 non_mesh_items: IndexMap::default(),
1030 batch_sets: match gpu_preprocessing {
1031 GpuPreprocessingMode::Culling => {
1032 BinnedRenderPhaseBatchSets::MultidrawIndirect(vec![])
1033 }
1034 GpuPreprocessingMode::PreprocessingOnly => {
1035 BinnedRenderPhaseBatchSets::Direct(vec![])
1036 }
1037 GpuPreprocessingMode::None => BinnedRenderPhaseBatchSets::DynamicUniforms(vec![]),
1038 },
1039 cached_entity_bin_keys: IndexMap::default(),
1040 valid_cached_entity_bin_keys: FixedBitSet::new(),
1041 entities_that_changed_bins: vec![],
1042 gpu_preprocessing_mode: gpu_preprocessing,
1043 }
1044 }
1045}
1046
1047impl UnbatchableBinnedEntityIndexSet {
1048 /// Returns the [`UnbatchableBinnedEntityIndices`] for the given entity.
1049 fn indices_for_entity_index(
1050 &self,
1051 entity_index: u32,
1052 ) -> Option<UnbatchableBinnedEntityIndices> {
1053 match self {
1054 UnbatchableBinnedEntityIndexSet::NoEntities => None,
1055 UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. }
1056 if entity_index >= instance_range.len() as u32 =>
1057 {
1058 None
1059 }
1060 UnbatchableBinnedEntityIndexSet::Sparse {
1061 instance_range,
1062 first_indirect_parameters_index: None,
1063 } => Some(UnbatchableBinnedEntityIndices {
1064 instance_index: instance_range.start + entity_index,
1065 extra_index: PhaseItemExtraIndex::None,
1066 }),
1067 UnbatchableBinnedEntityIndexSet::Sparse {
1068 instance_range,
1069 first_indirect_parameters_index: Some(first_indirect_parameters_index),
1070 } => {
1071 let first_indirect_parameters_index_for_this_batch =
1072 u32::from(*first_indirect_parameters_index) + entity_index;
1073 Some(UnbatchableBinnedEntityIndices {
1074 instance_index: instance_range.start + entity_index,
1075 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1076 range: first_indirect_parameters_index_for_this_batch
1077 ..(first_indirect_parameters_index_for_this_batch + 1),
1078 batch_set_index: None,
1079 },
1080 })
1081 }
1082 UnbatchableBinnedEntityIndexSet::Dense(indices) => {
1083 indices.get(entity_index as usize).cloned()
1084 }
1085 }
1086 }
1087}
1088
1089/// A convenient abstraction for adding all the systems necessary for a binned
1090/// render phase to the render app.
1091///
1092/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1093/// 3D PBR meshes.
1094pub struct BinnedRenderPhasePlugin<BPI, GFBD>
1095where
1096 BPI: BinnedPhaseItem,
1097 GFBD: GetFullBatchData,
1098{
1099 /// Debugging flags that can optionally be set when constructing the renderer.
1100 pub debug_flags: RenderDebugFlags,
1101 phantom: PhantomData<(BPI, GFBD)>,
1102}
1103
1104impl<BPI, GFBD> BinnedRenderPhasePlugin<BPI, GFBD>
1105where
1106 BPI: BinnedPhaseItem,
1107 GFBD: GetFullBatchData,
1108{
1109 pub fn new(debug_flags: RenderDebugFlags) -> Self {
1110 Self {
1111 debug_flags,
1112 phantom: PhantomData,
1113 }
1114 }
1115}
1116
1117impl<BPI, GFBD> Plugin for BinnedRenderPhasePlugin<BPI, GFBD>
1118where
1119 BPI: BinnedPhaseItem,
1120 GFBD: GetFullBatchData + Sync + Send + 'static,
1121{
1122 fn build(&self, app: &mut App) {
1123 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1124 return;
1125 };
1126
1127 render_app
1128 .init_resource::<ViewBinnedRenderPhases<BPI>>()
1129 .init_resource::<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>()
1130 .insert_resource(PhaseIndirectParametersBuffers::<BPI>::new(
1131 self.debug_flags
1132 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
1133 ))
1134 .add_systems(
1135 Render,
1136 (
1137 batching::sort_binned_render_phase::<BPI>.in_set(RenderSet::PhaseSort),
1138 (
1139 no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1140 .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1141 gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1142 .run_if(
1143 resource_exists::<
1144 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1145 >,
1146 ),
1147 )
1148 .in_set(RenderSet::PrepareResources),
1149 sweep_old_entities::<BPI>.in_set(RenderSet::QueueSweep),
1150 gpu_preprocessing::collect_buffers_for_phase::<BPI, GFBD>
1151 .run_if(
1152 resource_exists::<
1153 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1154 >,
1155 )
1156 .in_set(RenderSet::PrepareResourcesCollectPhaseBuffers),
1157 ),
1158 );
1159 }
1160}
1161
1162/// Stores the rendering instructions for a single phase that sorts items in all
1163/// views.
1164///
1165/// They're cleared out every frame, but storing them in a resource like this
1166/// allows us to reuse allocations.
1167#[derive(Resource, Deref, DerefMut)]
1168pub struct ViewSortedRenderPhases<SPI>(pub HashMap<RetainedViewEntity, SortedRenderPhase<SPI>>)
1169where
1170 SPI: SortedPhaseItem;
1171
1172impl<SPI> Default for ViewSortedRenderPhases<SPI>
1173where
1174 SPI: SortedPhaseItem,
1175{
1176 fn default() -> Self {
1177 Self(default())
1178 }
1179}
1180
1181impl<SPI> ViewSortedRenderPhases<SPI>
1182where
1183 SPI: SortedPhaseItem,
1184{
1185 pub fn insert_or_clear(&mut self, retained_view_entity: RetainedViewEntity) {
1186 match self.entry(retained_view_entity) {
1187 Entry::Occupied(mut entry) => entry.get_mut().clear(),
1188 Entry::Vacant(entry) => {
1189 entry.insert(default());
1190 }
1191 }
1192 }
1193}
1194
1195/// A convenient abstraction for adding all the systems necessary for a sorted
1196/// render phase to the render app.
1197///
1198/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1199/// 3D PBR meshes.
1200pub struct SortedRenderPhasePlugin<SPI, GFBD>
1201where
1202 SPI: SortedPhaseItem,
1203 GFBD: GetFullBatchData,
1204{
1205 /// Debugging flags that can optionally be set when constructing the renderer.
1206 pub debug_flags: RenderDebugFlags,
1207 phantom: PhantomData<(SPI, GFBD)>,
1208}
1209
1210impl<SPI, GFBD> SortedRenderPhasePlugin<SPI, GFBD>
1211where
1212 SPI: SortedPhaseItem,
1213 GFBD: GetFullBatchData,
1214{
1215 pub fn new(debug_flags: RenderDebugFlags) -> Self {
1216 Self {
1217 debug_flags,
1218 phantom: PhantomData,
1219 }
1220 }
1221}
1222
1223impl<SPI, GFBD> Plugin for SortedRenderPhasePlugin<SPI, GFBD>
1224where
1225 SPI: SortedPhaseItem + CachedRenderPipelinePhaseItem,
1226 GFBD: GetFullBatchData + Sync + Send + 'static,
1227{
1228 fn build(&self, app: &mut App) {
1229 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1230 return;
1231 };
1232
1233 render_app
1234 .init_resource::<ViewSortedRenderPhases<SPI>>()
1235 .init_resource::<PhaseBatchedInstanceBuffers<SPI, GFBD::BufferData>>()
1236 .insert_resource(PhaseIndirectParametersBuffers::<SPI>::new(
1237 self.debug_flags
1238 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
1239 ))
1240 .add_systems(
1241 Render,
1242 (
1243 (
1244 no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1245 .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1246 gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1247 .run_if(
1248 resource_exists::<
1249 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1250 >,
1251 ),
1252 )
1253 .in_set(RenderSet::PrepareResources),
1254 gpu_preprocessing::collect_buffers_for_phase::<SPI, GFBD>
1255 .run_if(
1256 resource_exists::<
1257 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1258 >,
1259 )
1260 .in_set(RenderSet::PrepareResourcesCollectPhaseBuffers),
1261 ),
1262 );
1263 }
1264}
1265
1266impl UnbatchableBinnedEntityIndexSet {
1267 /// Adds a new entity to the list of unbatchable binned entities.
1268 pub fn add(&mut self, indices: UnbatchableBinnedEntityIndices) {
1269 match self {
1270 UnbatchableBinnedEntityIndexSet::NoEntities => {
1271 match indices.extra_index {
1272 PhaseItemExtraIndex::DynamicOffset(_) => {
1273 // This is the first entity we've seen, and we don't have
1274 // compute shaders. Initialize an array.
1275 *self = UnbatchableBinnedEntityIndexSet::Dense(vec![indices]);
1276 }
1277 PhaseItemExtraIndex::None => {
1278 // This is the first entity we've seen, and we have compute
1279 // shaders. Initialize the fast path.
1280 *self = UnbatchableBinnedEntityIndexSet::Sparse {
1281 instance_range: indices.instance_index..indices.instance_index + 1,
1282 first_indirect_parameters_index: None,
1283 }
1284 }
1285 PhaseItemExtraIndex::IndirectParametersIndex {
1286 range: ref indirect_parameters_index,
1287 ..
1288 } => {
1289 // This is the first entity we've seen, and we have compute
1290 // shaders. Initialize the fast path.
1291 *self = UnbatchableBinnedEntityIndexSet::Sparse {
1292 instance_range: indices.instance_index..indices.instance_index + 1,
1293 first_indirect_parameters_index: NonMaxU32::new(
1294 indirect_parameters_index.start,
1295 ),
1296 }
1297 }
1298 }
1299 }
1300
1301 UnbatchableBinnedEntityIndexSet::Sparse {
1302 instance_range,
1303 first_indirect_parameters_index,
1304 } if instance_range.end == indices.instance_index
1305 && ((first_indirect_parameters_index.is_none()
1306 && indices.extra_index == PhaseItemExtraIndex::None)
1307 || first_indirect_parameters_index.is_some_and(
1308 |first_indirect_parameters_index| match indices.extra_index {
1309 PhaseItemExtraIndex::IndirectParametersIndex {
1310 range: ref this_range,
1311 ..
1312 } => {
1313 u32::from(first_indirect_parameters_index) + instance_range.end
1314 - instance_range.start
1315 == this_range.start
1316 }
1317 PhaseItemExtraIndex::DynamicOffset(_) | PhaseItemExtraIndex::None => {
1318 false
1319 }
1320 },
1321 )) =>
1322 {
1323 // This is the normal case on non-WebGL 2.
1324 instance_range.end += 1;
1325 }
1326
1327 UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. } => {
1328 // We thought we were in non-WebGL 2 mode, but we got a dynamic
1329 // offset or non-contiguous index anyway. This shouldn't happen,
1330 // but let's go ahead and do the sensible thing anyhow: demote
1331 // the compressed `NoDynamicOffsets` field to the full
1332 // `DynamicOffsets` array.
1333 warn!(
1334 "Unbatchable binned entity index set was demoted from sparse to dense. \
1335 This is a bug in the renderer. Please report it.",
1336 );
1337 let new_dynamic_offsets = (0..instance_range.len() as u32)
1338 .flat_map(|entity_index| self.indices_for_entity_index(entity_index))
1339 .chain(iter::once(indices))
1340 .collect();
1341 *self = UnbatchableBinnedEntityIndexSet::Dense(new_dynamic_offsets);
1342 }
1343
1344 UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => {
1345 dense_indices.push(indices);
1346 }
1347 }
1348 }
1349
1350 /// Clears the unbatchable binned entity index set.
1351 fn clear(&mut self) {
1352 match self {
1353 UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => dense_indices.clear(),
1354 UnbatchableBinnedEntityIndexSet::Sparse { .. } => {
1355 *self = UnbatchableBinnedEntityIndexSet::NoEntities;
1356 }
1357 _ => {}
1358 }
1359 }
1360}
1361
1362/// A collection of all items to be rendered that will be encoded to GPU
1363/// commands for a single render phase for a single view.
1364///
1365/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
1366/// They are used to queue entities for rendering.
1367/// Multiple phases might be required due to different sorting/batching behaviors
1368/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
1369/// the rendered texture of the previous phase (e.g. for screen-space reflections).
1370/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
1371/// The render pass might be reused for multiple phases to reduce GPU overhead.
1372///
1373/// This flavor of render phase is used only for meshes that need to be sorted
1374/// back-to-front, such as transparent meshes. For items that don't need strict
1375/// sorting, [`BinnedRenderPhase`] is preferred, for performance.
1376pub struct SortedRenderPhase<I>
1377where
1378 I: SortedPhaseItem,
1379{
1380 /// The items within this [`SortedRenderPhase`].
1381 pub items: Vec<I>,
1382}
1383
1384impl<I> Default for SortedRenderPhase<I>
1385where
1386 I: SortedPhaseItem,
1387{
1388 fn default() -> Self {
1389 Self { items: Vec::new() }
1390 }
1391}
1392
1393impl<I> SortedRenderPhase<I>
1394where
1395 I: SortedPhaseItem,
1396{
1397 /// Adds a [`PhaseItem`] to this render phase.
1398 #[inline]
1399 pub fn add(&mut self, item: I) {
1400 self.items.push(item);
1401 }
1402
1403 /// Removes all [`PhaseItem`]s from this render phase.
1404 #[inline]
1405 pub fn clear(&mut self) {
1406 self.items.clear();
1407 }
1408
1409 /// Sorts all of its [`PhaseItem`]s.
1410 pub fn sort(&mut self) {
1411 I::sort(&mut self.items);
1412 }
1413
1414 /// An [`Iterator`] through the associated [`Entity`] for each [`PhaseItem`] in order.
1415 #[inline]
1416 pub fn iter_entities(&'_ self) -> impl Iterator<Item = Entity> + '_ {
1417 self.items.iter().map(PhaseItem::entity)
1418 }
1419
1420 /// Renders all of its [`PhaseItem`]s using their corresponding draw functions.
1421 pub fn render<'w>(
1422 &self,
1423 render_pass: &mut TrackedRenderPass<'w>,
1424 world: &'w World,
1425 view: Entity,
1426 ) -> Result<(), DrawError> {
1427 self.render_range(render_pass, world, view, ..)
1428 }
1429
1430 /// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
1431 pub fn render_range<'w>(
1432 &self,
1433 render_pass: &mut TrackedRenderPass<'w>,
1434 world: &'w World,
1435 view: Entity,
1436 range: impl SliceIndex<[I], Output = [I]>,
1437 ) -> Result<(), DrawError> {
1438 let items = self
1439 .items
1440 .get(range)
1441 .expect("`Range` provided to `render_range()` is out of bounds");
1442
1443 let draw_functions = world.resource::<DrawFunctions<I>>();
1444 let mut draw_functions = draw_functions.write();
1445 draw_functions.prepare(world);
1446
1447 let mut index = 0;
1448 while index < items.len() {
1449 let item = &items[index];
1450 let batch_range = item.batch_range();
1451 if batch_range.is_empty() {
1452 index += 1;
1453 } else {
1454 let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
1455 draw_function.draw(world, render_pass, view, item)?;
1456 index += batch_range.len();
1457 }
1458 }
1459 Ok(())
1460 }
1461}
1462
1463/// An item (entity of the render world) which will be drawn to a texture or the screen,
1464/// as part of a render phase.
1465///
1466/// The data required for rendering an entity is extracted from the main world in the
1467/// [`ExtractSchedule`](crate::ExtractSchedule).
1468/// Then it has to be queued up for rendering during the [`RenderSet::Queue`],
1469/// by adding a corresponding phase item to a render phase.
1470/// Afterwards it will be possibly sorted and rendered automatically in the
1471/// [`RenderSet::PhaseSort`] and [`RenderSet::Render`], respectively.
1472///
1473/// `PhaseItem`s come in two flavors: [`BinnedPhaseItem`]s and
1474/// [`SortedPhaseItem`]s.
1475///
1476/// * Binned phase items have a `BinKey` which specifies what bin they're to be
1477/// placed in. All items in the same bin are eligible to be batched together.
1478/// The `BinKey`s are sorted, but the individual bin items aren't. Binned phase
1479/// items are good for opaque meshes, in which the order of rendering isn't
1480/// important. Generally, binned phase items are faster than sorted phase items.
1481///
1482/// * Sorted phase items, on the other hand, are placed into one large buffer
1483/// and then sorted all at once. This is needed for transparent meshes, which
1484/// have to be sorted back-to-front to render with the painter's algorithm.
1485/// These types of phase items are generally slower than binned phase items.
1486pub trait PhaseItem: Sized + Send + Sync + 'static {
1487 /// Whether or not this `PhaseItem` should be subjected to automatic batching. (Default: `true`)
1488 const AUTOMATIC_BATCHING: bool = true;
1489
1490 /// The corresponding entity that will be drawn.
1491 ///
1492 /// This is used to fetch the render data of the entity, required by the draw function,
1493 /// from the render world .
1494 fn entity(&self) -> Entity;
1495
1496 /// The main world entity represented by this `PhaseItem`.
1497 fn main_entity(&self) -> MainEntity;
1498
1499 /// Specifies the [`Draw`] function used to render the item.
1500 fn draw_function(&self) -> DrawFunctionId;
1501
1502 /// The range of instances that the batch covers. After doing a batched draw, batch range
1503 /// length phase items will be skipped. This design is to avoid having to restructure the
1504 /// render phase unnecessarily.
1505 fn batch_range(&self) -> &Range<u32>;
1506 fn batch_range_mut(&mut self) -> &mut Range<u32>;
1507
1508 /// Returns the [`PhaseItemExtraIndex`].
1509 ///
1510 /// If present, this is either a dynamic offset or an indirect parameters
1511 /// index.
1512 fn extra_index(&self) -> PhaseItemExtraIndex;
1513
1514 /// Returns a pair of mutable references to both the batch range and extra
1515 /// index.
1516 fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex);
1517}
1518
1519/// The "extra index" associated with some [`PhaseItem`]s, alongside the
1520/// indirect instance index.
1521///
1522/// Sometimes phase items require another index in addition to the range of
1523/// instances they already have. These can be:
1524///
1525/// * The *dynamic offset*: a `wgpu` dynamic offset into the uniform buffer of
1526/// instance data. This is used on platforms that don't support storage
1527/// buffers, to work around uniform buffer size limitations.
1528///
1529/// * The *indirect parameters index*: an index into the buffer that specifies
1530/// the indirect parameters for this [`PhaseItem`]'s drawcall. This is used when
1531/// indirect mode is on (as used for GPU culling).
1532///
1533/// Note that our indirect draw functionality requires storage buffers, so it's
1534/// impossible to have both a dynamic offset and an indirect parameters index.
1535/// This convenient fact allows us to pack both indices into a single `u32`.
1536#[derive(Clone, PartialEq, Eq, Hash, Debug)]
1537pub enum PhaseItemExtraIndex {
1538 /// No extra index is present.
1539 None,
1540 /// A `wgpu` dynamic offset into the uniform buffer of instance data. This
1541 /// is used on platforms that don't support storage buffers, to work around
1542 /// uniform buffer size limitations.
1543 DynamicOffset(u32),
1544 /// An index into the buffer that specifies the indirect parameters for this
1545 /// [`PhaseItem`]'s drawcall. This is used when indirect mode is on (as used
1546 /// for GPU culling).
1547 IndirectParametersIndex {
1548 /// The range of indirect parameters within the indirect parameters array.
1549 ///
1550 /// If we're using `multi_draw_indirect_count`, this specifies the
1551 /// maximum range of indirect parameters within that array. If batches
1552 /// are ultimately culled out on the GPU, the actual number of draw
1553 /// commands might be lower than the length of this range.
1554 range: Range<u32>,
1555 /// If `multi_draw_indirect_count` is in use, and this phase item is
1556 /// part of a batch set, specifies the index of the batch set that this
1557 /// phase item is a part of.
1558 ///
1559 /// If `multi_draw_indirect_count` isn't in use, or this phase item
1560 /// isn't part of a batch set, this is `None`.
1561 batch_set_index: Option<NonMaxU32>,
1562 },
1563}
1564
1565impl PhaseItemExtraIndex {
1566 /// Returns either an indirect parameters index or
1567 /// [`PhaseItemExtraIndex::None`], as appropriate.
1568 pub fn maybe_indirect_parameters_index(
1569 indirect_parameters_index: Option<NonMaxU32>,
1570 ) -> PhaseItemExtraIndex {
1571 match indirect_parameters_index {
1572 Some(indirect_parameters_index) => PhaseItemExtraIndex::IndirectParametersIndex {
1573 range: u32::from(indirect_parameters_index)
1574 ..(u32::from(indirect_parameters_index) + 1),
1575 batch_set_index: None,
1576 },
1577 None => PhaseItemExtraIndex::None,
1578 }
1579 }
1580
1581 /// Returns either a dynamic offset index or [`PhaseItemExtraIndex::None`],
1582 /// as appropriate.
1583 pub fn maybe_dynamic_offset(dynamic_offset: Option<NonMaxU32>) -> PhaseItemExtraIndex {
1584 match dynamic_offset {
1585 Some(dynamic_offset) => PhaseItemExtraIndex::DynamicOffset(dynamic_offset.into()),
1586 None => PhaseItemExtraIndex::None,
1587 }
1588 }
1589}
1590
1591/// Represents phase items that are placed into bins. The `BinKey` specifies
1592/// which bin they're to be placed in. Bin keys are sorted, and items within the
1593/// same bin are eligible to be batched together. The elements within the bins
1594/// aren't themselves sorted.
1595///
1596/// An example of a binned phase item is `Opaque3d`, for which the rendering
1597/// order isn't critical.
1598pub trait BinnedPhaseItem: PhaseItem {
1599 /// The key used for binning [`PhaseItem`]s into bins. Order the members of
1600 /// [`BinnedPhaseItem::BinKey`] by the order of binding for best
1601 /// performance. For example, pipeline id, draw function id, mesh asset id,
1602 /// lowest variable bind group id such as the material bind group id, and
1603 /// its dynamic offsets if any, next bind group and offsets, etc. This
1604 /// reduces the need for rebinding between bins and improves performance.
1605 type BinKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash;
1606
1607 /// The key used to combine batches into batch sets.
1608 ///
1609 /// A *batch set* is a set of meshes that can potentially be multi-drawn
1610 /// together.
1611 type BatchSetKey: PhaseItemBatchSetKey;
1612
1613 /// Creates a new binned phase item from the key and per-entity data.
1614 ///
1615 /// Unlike [`SortedPhaseItem`]s, this is generally called "just in time"
1616 /// before rendering. The resulting phase item isn't stored in any data
1617 /// structures, resulting in significant memory savings.
1618 fn new(
1619 batch_set_key: Self::BatchSetKey,
1620 bin_key: Self::BinKey,
1621 representative_entity: (Entity, MainEntity),
1622 batch_range: Range<u32>,
1623 extra_index: PhaseItemExtraIndex,
1624 ) -> Self;
1625}
1626
1627/// A key used to combine batches into batch sets.
1628///
1629/// A *batch set* is a set of meshes that can potentially be multi-drawn
1630/// together.
1631pub trait PhaseItemBatchSetKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash {
1632 /// Returns true if this batch set key describes indexed meshes or false if
1633 /// it describes non-indexed meshes.
1634 ///
1635 /// Bevy uses this in order to determine which kind of indirect draw
1636 /// parameters to use, if indirect drawing is enabled.
1637 fn indexed(&self) -> bool;
1638}
1639
1640/// Represents phase items that must be sorted. The `SortKey` specifies the
1641/// order that these items are drawn in. These are placed into a single array,
1642/// and the array as a whole is then sorted.
1643///
1644/// An example of a sorted phase item is `Transparent3d`, which must be sorted
1645/// back to front in order to correctly render with the painter's algorithm.
1646pub trait SortedPhaseItem: PhaseItem {
1647 /// The type used for ordering the items. The smallest values are drawn first.
1648 /// This order can be calculated using the [`ViewRangefinder3d`],
1649 /// based on the view-space `Z` value of the corresponding view matrix.
1650 type SortKey: Ord;
1651
1652 /// Determines the order in which the items are drawn.
1653 fn sort_key(&self) -> Self::SortKey;
1654
1655 /// Sorts a slice of phase items into render order. Generally if the same type
1656 /// is batched this should use a stable sort like [`slice::sort_by_key`].
1657 /// In almost all other cases, this should not be altered from the default,
1658 /// which uses an unstable sort, as this provides the best balance of CPU and GPU
1659 /// performance.
1660 ///
1661 /// Implementers can optionally not sort the list at all. This is generally advisable if and
1662 /// only if the renderer supports a depth prepass, which is by default not supported by
1663 /// the rest of Bevy's first party rendering crates. Even then, this may have a negative
1664 /// impact on GPU-side performance due to overdraw.
1665 ///
1666 /// It's advised to always profile for performance changes when changing this implementation.
1667 #[inline]
1668 fn sort(items: &mut [Self]) {
1669 items.sort_unstable_by_key(Self::sort_key);
1670 }
1671
1672 /// Whether this phase item targets indexed meshes (those with both vertex
1673 /// and index buffers as opposed to just vertex buffers).
1674 ///
1675 /// Bevy needs this information in order to properly group phase items
1676 /// together for multi-draw indirect, because the GPU layout of indirect
1677 /// commands differs between indexed and non-indexed meshes.
1678 ///
1679 /// If you're implementing a custom phase item that doesn't describe a mesh,
1680 /// you can safely return false here.
1681 fn indexed(&self) -> bool;
1682}
1683
1684/// A [`PhaseItem`] item, that automatically sets the appropriate render pipeline,
1685/// cached in the [`PipelineCache`].
1686///
1687/// You can use the [`SetItemPipeline`] render command to set the pipeline for this item.
1688pub trait CachedRenderPipelinePhaseItem: PhaseItem {
1689 /// The id of the render pipeline, cached in the [`PipelineCache`], that will be used to draw
1690 /// this phase item.
1691 fn cached_pipeline(&self) -> CachedRenderPipelineId;
1692}
1693
1694/// A [`RenderCommand`] that sets the pipeline for the [`CachedRenderPipelinePhaseItem`].
1695pub struct SetItemPipeline;
1696
1697impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
1698 type Param = SRes<PipelineCache>;
1699 type ViewQuery = ();
1700 type ItemQuery = ();
1701 #[inline]
1702 fn render<'w>(
1703 item: &P,
1704 _view: (),
1705 _entity: Option<()>,
1706 pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
1707 pass: &mut TrackedRenderPass<'w>,
1708 ) -> RenderCommandResult {
1709 if let Some(pipeline) = pipeline_cache
1710 .into_inner()
1711 .get_render_pipeline(item.cached_pipeline())
1712 {
1713 pass.set_render_pipeline(pipeline);
1714 RenderCommandResult::Success
1715 } else {
1716 RenderCommandResult::Skip
1717 }
1718 }
1719}
1720
1721/// This system sorts the [`PhaseItem`]s of all [`SortedRenderPhase`]s of this
1722/// type.
1723pub fn sort_phase_system<I>(mut render_phases: ResMut<ViewSortedRenderPhases<I>>)
1724where
1725 I: SortedPhaseItem,
1726{
1727 for phase in render_phases.values_mut() {
1728 phase.sort();
1729 }
1730}
1731
1732/// Removes entities that became invisible or changed phases from the bins.
1733///
1734/// This must run after queuing.
1735pub fn sweep_old_entities<BPI>(mut render_phases: ResMut<ViewBinnedRenderPhases<BPI>>)
1736where
1737 BPI: BinnedPhaseItem,
1738{
1739 for phase in render_phases.0.values_mut() {
1740 phase.sweep_old_entities();
1741 }
1742}
1743
1744impl BinnedRenderPhaseType {
1745 pub fn mesh(
1746 batchable: bool,
1747 gpu_preprocessing_support: &GpuPreprocessingSupport,
1748 ) -> BinnedRenderPhaseType {
1749 match (batchable, gpu_preprocessing_support.max_supported_mode) {
1750 (true, GpuPreprocessingMode::Culling) => BinnedRenderPhaseType::MultidrawableMesh,
1751 (true, _) => BinnedRenderPhaseType::BatchableMesh,
1752 (false, _) => BinnedRenderPhaseType::UnbatchableMesh,
1753 }
1754 }
1755}
1756
1757impl RenderBin {
1758 /// Creates a [`RenderBin`] containing a single entity.
1759 fn from_entity(entity: MainEntity, uniform_index: InputUniformIndex) -> RenderBin {
1760 let mut entities = IndexMap::default();
1761 entities.insert(entity, uniform_index);
1762 RenderBin { entities }
1763 }
1764
1765 /// Inserts an entity into the bin.
1766 fn insert(&mut self, entity: MainEntity, uniform_index: InputUniformIndex) {
1767 self.entities.insert(entity, uniform_index);
1768 }
1769
1770 /// Removes an entity from the bin.
1771 fn remove(&mut self, entity_to_remove: MainEntity) {
1772 self.entities.swap_remove(&entity_to_remove);
1773 }
1774
1775 /// Returns true if the bin contains no entities.
1776 fn is_empty(&self) -> bool {
1777 self.entities.is_empty()
1778 }
1779
1780 /// Returns the [`IndexMap`] containing all the entities in the bin, along
1781 /// with the cached [`InputUniformIndex`] of each.
1782 #[inline]
1783 pub fn entities(&self) -> &IndexMap<MainEntity, InputUniformIndex, EntityHash> {
1784 &self.entities
1785 }
1786}
1787
1788/// An iterator that efficiently finds the indices of all zero bits in a
1789/// [`FixedBitSet`] and returns them in reverse order.
1790///
1791/// [`FixedBitSet`] doesn't natively offer this functionality, so we have to
1792/// implement it ourselves.
1793#[derive(Debug)]
1794struct ReverseFixedBitSetZeroesIterator<'a> {
1795 /// The bit set.
1796 bitset: &'a FixedBitSet,
1797 /// The next bit index we're going to scan when [`Iterator::next`] is
1798 /// called.
1799 bit_index: isize,
1800}
1801
1802impl<'a> ReverseFixedBitSetZeroesIterator<'a> {
1803 fn new(bitset: &'a FixedBitSet) -> ReverseFixedBitSetZeroesIterator<'a> {
1804 ReverseFixedBitSetZeroesIterator {
1805 bitset,
1806 bit_index: (bitset.len() as isize) - 1,
1807 }
1808 }
1809}
1810
1811impl<'a> Iterator for ReverseFixedBitSetZeroesIterator<'a> {
1812 type Item = usize;
1813
1814 fn next(&mut self) -> Option<usize> {
1815 while self.bit_index >= 0 {
1816 // Unpack the bit index into block and bit.
1817 let block_index = self.bit_index / (Block::BITS as isize);
1818 let bit_pos = self.bit_index % (Block::BITS as isize);
1819
1820 // Grab the block. Mask off all bits above the one we're scanning
1821 // from by setting them all to 1.
1822 let mut block = self.bitset.as_slice()[block_index as usize];
1823 if bit_pos + 1 < (Block::BITS as isize) {
1824 block |= (!0) << (bit_pos + 1);
1825 }
1826
1827 // Search for the next unset bit. Note that the `leading_ones`
1828 // function counts from the MSB to the LSB, so we need to flip it to
1829 // get the bit number.
1830 let pos = (Block::BITS as isize) - (block.leading_ones() as isize) - 1;
1831
1832 // If we found an unset bit, return it.
1833 if pos != -1 {
1834 let result = block_index * (Block::BITS as isize) + pos;
1835 self.bit_index = result - 1;
1836 return Some(result as usize);
1837 }
1838
1839 // Otherwise, go to the previous block.
1840 self.bit_index = block_index * (Block::BITS as isize) - 1;
1841 }
1842
1843 None
1844 }
1845}
1846
1847#[cfg(test)]
1848mod test {
1849 use super::ReverseFixedBitSetZeroesIterator;
1850 use fixedbitset::FixedBitSet;
1851 use proptest::{collection::vec, prop_assert_eq, proptest};
1852
1853 proptest! {
1854 #[test]
1855 fn reverse_fixed_bit_set_zeroes_iterator(
1856 bits in vec(0usize..1024usize, 0usize..1024usize),
1857 size in 0usize..1024usize,
1858 ) {
1859 // Build a random bit set.
1860 let mut bitset = FixedBitSet::new();
1861 bitset.grow(size);
1862 for bit in bits {
1863 if bit < size {
1864 bitset.set(bit, true);
1865 }
1866 }
1867
1868 // Iterate over the bit set backwards in a naive way, and check that
1869 // that iteration sequence corresponds to the optimized one.
1870 let mut iter = ReverseFixedBitSetZeroesIterator::new(&bitset);
1871 for bit_index in (0..size).rev() {
1872 if !bitset.contains(bit_index) {
1873 prop_assert_eq!(iter.next(), Some(bit_index));
1874 }
1875 }
1876
1877 prop_assert_eq!(iter.next(), None);
1878 }
1879 }
1880}