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}