Skip to main content

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 [`RenderSystems::Queue`].
14//! After that the render phase sorts them in the [`RenderSystems::PhaseSort`].
15//! Finally the items are rendered using a single [`TrackedRenderPass`], during
16//! the [`RenderSystems::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::entity::EntityHash;
34use bevy_platform::collections::{hash_map::Entry, HashMap};
35use bevy_utils::default;
36use bytemuck::{Pod, Zeroable};
37pub use draw::*;
38pub use draw_state::*;
39use encase::ShaderType;
40use encase::{internal::WriteInto, ShaderSize};
41use indexmap::IndexMap;
42use nonmax::NonMaxU32;
43pub use rangefinder::*;
44use wgpu::{BufferUsages, Features};
45
46use crate::batching::gpu_preprocessing::{
47    GpuPreprocessingMode, GpuPreprocessingSupport, PhaseBatchedInstanceBuffers,
48    PhaseIndirectParametersBuffers,
49};
50use crate::render_resource::RawBufferVec;
51use crate::renderer::RenderDevice;
52use crate::sync_world::{MainEntity, MainEntityHashMap};
53use crate::view::{ExtractedView, RetainedViewEntity};
54use crate::RenderDebugFlags;
55use bevy_material::descriptor::CachedRenderPipelineId;
56
57use crate::{
58    batching::{
59        self,
60        gpu_preprocessing::{self, BatchedInstanceBuffers},
61        no_gpu_preprocessing::{self, BatchedInstanceBuffer},
62        GetFullBatchData,
63    },
64    render_resource::{GpuArrayBufferIndex, PipelineCache},
65    GpuResourceAppExt, Render, RenderApp, RenderSystems,
66};
67use bevy_ecs::{
68    prelude::*,
69    system::{lifetimeless::SRes, SystemParamItem},
70};
71use bevy_log::warn;
72pub use bevy_material::labels::DrawFunctionId;
73pub use bevy_material_macros::DrawFunctionLabel;
74pub use bevy_material_macros::ShaderLabel;
75use bevy_render::renderer::RenderAdapterInfo;
76use core::{
77    fmt::Debug,
78    hash::Hash,
79    iter,
80    marker::PhantomData,
81    mem,
82    ops::{Range, RangeBounds},
83};
84use smallvec::SmallVec;
85
86/// Stores the rendering instructions for a single phase that uses bins in all
87/// views.
88///
89/// They're cleared out every frame, but storing them in a resource like this
90/// allows us to reuse allocations.
91#[derive(impl<BPI> bevy_ecs::resource::Resource for ViewBinnedRenderPhases<BPI> where
    BPI: BinnedPhaseItem, Self: ::core::marker::Send + ::core::marker::Sync +
    'static {}Resource, impl<BPI> ::core::ops::Deref for ViewBinnedRenderPhases<BPI> where
    BPI: BinnedPhaseItem {
    type Target = HashMap<RetainedViewEntity, BinnedRenderPhase<BPI>>;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl<BPI> ::core::ops::DerefMut for ViewBinnedRenderPhases<BPI> where
    BPI: BinnedPhaseItem {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
92pub struct ViewBinnedRenderPhases<BPI>(pub HashMap<RetainedViewEntity, BinnedRenderPhase<BPI>>)
93where
94    BPI: BinnedPhaseItem;
95
96/// A collection of all rendering instructions, that will be executed by the GPU, for a
97/// single render phase for a single view.
98///
99/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
100/// They are used to queue entities for rendering.
101/// Multiple phases might be required due to different sorting/batching behaviors
102/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
103/// the rendered texture of the previous phase (e.g. for screen-space reflections).
104/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
105/// The render pass might be reused for multiple phases to reduce GPU overhead.
106///
107/// This flavor of render phase is used for phases in which the ordering is less
108/// critical: for example, `Opaque3d`. It's generally faster than the
109/// alternative [`SortedRenderPhase`].
110pub struct BinnedRenderPhase<BPI>
111where
112    BPI: BinnedPhaseItem,
113{
114    /// The multidrawable bins.
115    ///
116    /// Each batch set key maps to a *batch set*, which in this case is a set of
117    /// meshes that can be drawn together in one multidraw call. Each batch set
118    /// is subdivided into *bins*, each of which represents a particular mesh.
119    /// Each bin contains the entity IDs of instances of that mesh.
120    ///
121    /// So, for example, if there are two cubes and a sphere present in the
122    /// scene, we would generally have one batch set containing two bins,
123    /// assuming that the cubes and sphere meshes are allocated together and use
124    /// the same pipeline. The first bin, corresponding to the cubes, will have
125    /// two entities in it. The second bin, corresponding to the sphere, will
126    /// have one entity in it.
127    pub multidrawable_meshes: IndexMap<BPI::BatchSetKey, RenderMultidrawableBatchSet<BPI>>,
128
129    /// The bins corresponding to batchable items that aren't multidrawable.
130    ///
131    /// For multidrawable entities, use `multidrawable_meshes`; for
132    /// unbatchable entities, use `unbatchable_values`.
133    pub batchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
134
135    /// The unbatchable bins.
136    ///
137    /// Each entity here is rendered in a separate drawcall.
138    pub unbatchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
139
140    /// Items in the bin that aren't meshes at all.
141    ///
142    /// Bevy itself doesn't place anything in this list, but plugins or your app
143    /// can in order to execute custom drawing commands. Draw functions for each
144    /// entity are simply called in order at rendering time.
145    ///
146    /// See the `custom_phase_item` example for an example of how to use this.
147    pub non_mesh_items: IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
148
149    /// Information on each batch set.
150    ///
151    /// A *batch set* is a set of entities that will be batched together unless
152    /// we're on a platform that doesn't support storage buffers (e.g. WebGL 2)
153    /// and differing dynamic uniform indices force us to break batches. On
154    /// platforms that support storage buffers, a batch set always consists of
155    /// at most one batch.
156    ///
157    /// Multidrawable entities come first, then batchable entities, then
158    /// unbatchable entities.
159    pub(crate) batch_sets: BinnedRenderPhaseBatchSets<BPI::BinKey>,
160
161    /// The batch and bin key for each entity.
162    ///
163    /// We retain these so that, when the entity changes, the methods that
164    /// remove items from bins can quickly find the bin each entity was located
165    /// in in order to remove it.
166    cached_entity_bin_keys: MainEntityHashMap<CachedBinnedEntity<BPI>>,
167
168    /// The gpu preprocessing mode configured for the view this phase is associated
169    /// with.
170    gpu_preprocessing_mode: GpuPreprocessingMode,
171}
172
173/// All entities that share a mesh and a material and can be batched as part of
174/// a [`BinnedRenderPhase`].
175#[derive(#[automatically_derived]
impl ::core::default::Default for RenderBin {
    #[inline]
    fn default() -> RenderBin {
        RenderBin { entities: ::core::default::Default::default() }
    }
}Default)]
176pub struct RenderBin {
177    /// A list of the entities in each bin, along with their cached
178    /// [`InputUniformIndex`].
179    entities: IndexMap<MainEntity, InputUniformIndex, EntityHash>,
180}
181
182/// Information about each bin that the [`RenderMultidrawableBatchSet`]
183/// maintains on the CPU.
184#[derive(#[automatically_derived]
impl ::core::default::Default for RenderMultidrawableBin {
    #[inline]
    fn default() -> RenderMultidrawableBin {
        RenderMultidrawableBin {
            entity_to_binned_mesh_instance_index: ::core::default::Default::default(),
        }
    }
}Default)]
185pub struct RenderMultidrawableBin {
186    /// The [`RenderBinnedMeshInstanceIndex`] of each entity in this bin.
187    ///
188    /// Note that [`RenderBinnedMeshInstanceIndex`]es aren't stable from frame
189    /// to frame. They can change as entities are added and removed.
190    pub(crate) entity_to_binned_mesh_instance_index:
191        MainEntityHashMap<RenderBinnedMeshInstanceIndex>,
192}
193
194impl RenderMultidrawableBin {
195    /// Creates a new, empty [`RenderMultidrawableBin`].
196    fn new() -> RenderMultidrawableBin {
197        RenderMultidrawableBin {
198            entity_to_binned_mesh_instance_index: HashMap::default(),
199        }
200    }
201
202    /// Returns true if this bin has no entities in it.
203    fn is_empty(&self) -> bool {
204        self.entity_to_binned_mesh_instance_index.is_empty()
205    }
206}
207
208/// The index of a mesh instance in the
209/// [`RenderMultidrawableBatchSetGpuBuffers::render_binned_mesh_instance_buffer`]
210/// and [`RenderMultidrawableBatchSet::render_binned_mesh_instances_cpu`]
211/// arrays.
212///
213/// These two arrays are parallel and always have the same length.
214///
215/// These binned mesh instance indices aren't stable from frame to frame; they
216/// can change as entities are added and removed from bins. To reference a mesh
217/// instance in a stable manner, simply use [`MainEntity`].
218#[derive(#[automatically_derived]
impl ::core::clone::Clone for RenderBinnedMeshInstanceIndex {
    #[inline]
    fn clone(&self) -> RenderBinnedMeshInstanceIndex {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for RenderBinnedMeshInstanceIndex { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for RenderBinnedMeshInstanceIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "RenderBinnedMeshInstanceIndex", &&self.0)
    }
}Debug, impl ::core::ops::Deref for RenderBinnedMeshInstanceIndex {
    type Target = u32;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for RenderBinnedMeshInstanceIndex {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
219pub(crate) struct RenderBinnedMeshInstanceIndex(pub(crate) u32);
220
221/// The GPU buffers that go along with [`RenderMultidrawableBatchSet`].
222///
223/// The bin unpacking shader uses these in order to produce
224/// `PreprocessWorkItem`s.
225///
226/// See the diagram in [`RenderMultidrawableBatchSet`] for a visual explanation
227/// of this data structure.
228pub struct RenderMultidrawableBatchSetGpuBuffers {
229    /// A mapping from each binned mesh instance
230    /// (`RenderBinnedMeshInstanceIndex`) to its input uniform index
231    /// ([`InputUniformIndex`]) and bin index (`RenderBinIndex`).
232    pub render_binned_mesh_instance_buffer: RawBufferVec<GpuRenderBinnedMeshInstance>,
233    /// A mapping from each `RenderBinnedMeshInstanceIndex` to the offset of its
234    /// indirect draw parameters.
235    pub bin_index_to_indirect_parameters_offset_buffer: RawBufferVec<u32>,
236}
237
238/// Information about each binned mesh instance that the
239/// [`RenderMultidrawableBatchSet`] keeps on CPU.
240#[derive(#[automatically_derived]
impl ::core::clone::Clone for CpuRenderBinnedMeshInstance {
    #[inline]
    fn clone(&self) -> CpuRenderBinnedMeshInstance {
        let _: ::core::clone::AssertParamIsClone<MainEntity>;
        let _: ::core::clone::AssertParamIsClone<RenderBinIndex>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CpuRenderBinnedMeshInstance { }Copy)]
241pub(crate) struct CpuRenderBinnedMeshInstance {
242    /// The entity associated with this mesh instance.
243    pub(crate) main_entity: MainEntity,
244
245    /// The index of the bin that the entity is in.
246    ///
247    /// Note that bin indices are stable from frame to frame.
248    bin_index: RenderBinIndex,
249}
250
251impl Default for CpuRenderBinnedMeshInstance {
252    fn default() -> Self {
253        CpuRenderBinnedMeshInstance {
254            main_entity: MainEntity::from(Entity::PLACEHOLDER),
255            bin_index: RenderBinIndex::default(),
256        }
257    }
258}
259
260impl RenderMultidrawableBatchSetGpuBuffers {
261    /// Creates a new set of GPU buffers for a multidrawable batch set.
262    fn new() -> RenderMultidrawableBatchSetGpuBuffers {
263        let mut render_bin_entry_buffer = RawBufferVec::new(BufferUsages::STORAGE);
264        render_bin_entry_buffer.set_label(Some("render bin entry buffer"));
265        let mut bin_index_to_indirect_parameters_offset_buffer =
266            RawBufferVec::new(BufferUsages::STORAGE);
267        bin_index_to_indirect_parameters_offset_buffer
268            .set_label(Some("bin index to indirect parameters offset buffer"));
269
270        RenderMultidrawableBatchSetGpuBuffers {
271            render_binned_mesh_instance_buffer: render_bin_entry_buffer,
272            bin_index_to_indirect_parameters_offset_buffer,
273        }
274    }
275
276    /// Inserts an entity into the GPU buffers.
277    fn insert(
278        &mut self,
279        bin: &mut RenderMultidrawableBin,
280        cpu_binned_mesh_instance_buffer: &mut Vec<CpuRenderBinnedMeshInstance>,
281        main_entity: MainEntity,
282        input_uniform_index: InputUniformIndex,
283        bin_index: RenderBinIndex,
284    ) {
285        // Creates a `GpuRenderBinnedMeshInstance`.
286        let gpu_render_bin_entry = GpuRenderBinnedMeshInstance {
287            input_uniform_index: input_uniform_index.0,
288            bin_index: bin_index.0,
289        };
290
291        // Fetch the index of this entity in the
292        // `render_binned_mesh_instance_buffer`. If there isn't one, then
293        // allocate one.
294        let render_binned_mesh_instance_buffer_index =
295            match bin.entity_to_binned_mesh_instance_index.entry(main_entity) {
296                Entry::Occupied(occupied_entry) => *occupied_entry.get(),
297                Entry::Vacant(vacant_entry) => {
298                    let render_bin_buffer_index = RenderBinnedMeshInstanceIndex(
299                        self.render_binned_mesh_instance_buffer
300                            .push(GpuRenderBinnedMeshInstance::default())
301                            as u32,
302                    );
303                    cpu_binned_mesh_instance_buffer.push(CpuRenderBinnedMeshInstance::default());
304                    vacant_entry.insert(render_bin_buffer_index);
305                    render_bin_buffer_index
306                }
307            };
308
309        // Place the entry in the instance buffer at the proper spot. Also, save
310        // the entity and bin index in the CPU-side array.
311        self.render_binned_mesh_instance_buffer.values_mut()
312            [render_binned_mesh_instance_buffer_index.0 as usize] = gpu_render_bin_entry;
313        cpu_binned_mesh_instance_buffer[render_binned_mesh_instance_buffer_index.0 as usize] =
314            CpuRenderBinnedMeshInstance {
315                main_entity,
316                bin_index,
317            };
318
319        // The GPU-side `render_binned_mesh_instance_buffer` and the CPU-side
320        // `cpu_binned_mesh_instance_buffer` are parallel arrays and must have
321        // the same length, so assert that in debug mode.
322        if true {
    match (&self.render_binned_mesh_instance_buffer.len(),
            &cpu_binned_mesh_instance_buffer.len()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(
323            self.render_binned_mesh_instance_buffer.len(),
324            cpu_binned_mesh_instance_buffer.len()
325        );
326    }
327
328    /// Removes an entity from a bin.
329    ///
330    /// The entity must be present in the bin, or a panic will occur.
331    ///
332    /// Because binned mesh instances are tightly packed in the buffers, we use
333    /// `swap_remove`, which swaps the last element to fill the place of the
334    /// entity that was removed. This might change the
335    /// [`RenderBinnedMeshInstanceIndex`] of some *other* entity, requiring the
336    /// caller to perform additional bookkeeping. This method returns the index
337    /// of the displaced entity, if there was one.
338    #[must_use]
339    fn remove(
340        &mut self,
341        bin: &mut RenderMultidrawableBin,
342        cpu_binned_mesh_instance_buffer: &mut Vec<CpuRenderBinnedMeshInstance>,
343        entity_to_remove: MainEntity,
344    ) -> Option<(RenderBinnedMeshInstanceIndex, CpuRenderBinnedMeshInstance)> {
345        // Remove the entity from the `entity_to_binned_mesh_instance_index`
346        // map.
347        let old_index = bin
348            .entity_to_binned_mesh_instance_index
349            .remove(&entity_to_remove)
350            .expect("Entity not in bin");
351
352        // Remove the entity from the reverse
353        // `render_binned_mesh_instance_buffer` list, as well
354        // as the parallel `render_binned_mesh_instance_buffer`.  Because binned
355        // mesh instance indices must be contiguous, this requires use of
356        // `swap_remove`.
357        cpu_binned_mesh_instance_buffer.swap_remove(old_index.0 as usize);
358        self.render_binned_mesh_instance_buffer
359            .swap_remove(old_index.0 as usize);
360
361        // Both `render_binned_mesh_instance_buffer` and
362        // `cpu_binned_mesh_instance_buffer` must be parallel arrays, so assert
363        // that they have the same length.
364        if true {
    match (&cpu_binned_mesh_instance_buffer.len(),
            &self.render_binned_mesh_instance_buffer.len()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(
365            cpu_binned_mesh_instance_buffer.len(),
366            self.render_binned_mesh_instance_buffer.len()
367        );
368
369        // If an entity was displaced (i.e. has a new binned mesh instance index
370        // now), then return that to the caller so that they can perform
371        // whatever bookkeeping is necessary.
372        cpu_binned_mesh_instance_buffer
373            .get(old_index.0 as usize)
374            .map(|entity_indices| (old_index, *entity_indices))
375    }
376}
377
378/// The index of a bin in a [`RenderMultidrawableBatchSet`].
379///
380/// This bin index is stable from frame to frame for bins that have at least one
381/// mesh instance in them, though it can be reused if bins are deleted.
382#[derive(#[automatically_derived]
impl ::core::clone::Clone for RenderBinIndex {
    #[inline]
    fn clone(&self) -> RenderBinIndex {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for RenderBinIndex { }Copy, #[automatically_derived]
impl ::core::default::Default for RenderBinIndex {
    #[inline]
    fn default() -> RenderBinIndex {
        RenderBinIndex(::core::default::Default::default())
    }
}Default, #[automatically_derived]
impl ::core::cmp::PartialEq for RenderBinIndex {
    #[inline]
    fn eq(&self, other: &RenderBinIndex) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for RenderBinIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "RenderBinIndex",
            &&self.0)
    }
}Debug, unsafe impl ::bytemuck::Pod for RenderBinIndex {}Pod, unsafe impl ::bytemuck::Zeroable for RenderBinIndex {}Zeroable, impl ::core::ops::Deref for RenderBinIndex {
    type Target = u32;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for RenderBinIndex {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
383#[repr(transparent)]
384pub(crate) struct RenderBinIndex(pub(crate) u32);
385
386/// A collection of mesh instances that can be drawn together, sorted into bins.
387///
388/// This data structure stores a list of entity indices corresponding to mesh
389/// instances, along with the bins they live in. Each bin contains the offset of
390/// the indirect parameters needed to draw that bin.
391///
392/// This data structure consists of both CPU and GPU parts. A schematic diagram
393/// of the data structure is as follows:
394///
395/// ```text
396///         ┌─
397///         │                ─────┬──────────────┬─────
398///         │                     │ Mesh Inst. 2 │
399///         │  Binned Mesh    ... ├──────────────┤ ...
400///         │  Instances          │ Entity 8     │
401///         │                ─────┴───┬──────────┴─────
402///         │                         │
403///         │                         │   ┌───────────────────────────────┐
404///         │                         │   │                               │
405///         │                         ▼   ▼                               │
406///         │               ┌───────┬───────┬───────┬─────                │
407///         │  Bins         │ Bin 0 │ Bin 1 │ Bin 2 │ ...                 │
408///         │               └───────┴───┬───┴───────┴─────                │
409///         │                           │                                 │
410///     CPU │                           │                                 │
411///         │  Entity-to-               │  ┌──────────┬──────────┬─────   │
412///         │  Binned-Mesh-             └─►│ Entity 3 │ Entity 8 │ ...    │
413///         │  Instance-                   └──────────┴──────┬───┴─────   │
414///         │  Index                                         │            │
415///         │                                                │            │
416///         │                                                │            │
417///         │                                                │            │
418///         │                                                │            │
419///         │  Indirect-     ┌───────┬───────┬───────┬─────  │            │
420///         │  Parameters-   │ IPO 0 │ IPO 1 │ IPO 2 │ ...   │            │
421///         │  Offset-to-    └───────┴───────┴───────┴─────  │            │
422///         │  Bin-Index                         ▲           │            │
423///         │                                    │           │            │
424///         └─                                   │           │            │
425///                                      ┌───────┘           │            │
426///         ┌─                           │                   │            │
427///         │                            │                   │            │
428///         │  Bin-to-                   ▼                   │            │
429///         │  Indirect-     ┌───────┬───────┬───────┬─────  │            │
430///         │  Parameters-   │ Bin 0 │ Bin 1 │ Bin 2 │ ...   │            │
431///         │  Offset        └───────┴───────┴───────┴─────  │            │
432///     GPU │  Buffer                                        │            │
433///         │                                                │            │
434///         │                                                ▼            │
435///         │  Binned Mesh   ─────┬──────────────┬──────────────┬─────    │
436///         │  Instance       ... │ Mesh Inst. 1 │ Mesh Inst. 2 │ ...     │
437///         │  Buffer        ─────┴──────────────┴───────────┬──┴─────    │
438///         │                                                │            │
439///         └─                                               └────────────┘
440/// ```
441pub struct RenderMultidrawableBatchSet<BPI>
442where
443    BPI: BinnedPhaseItem,
444{
445    /// The GPU buffers that store the instances in this batch set.
446    pub(crate) gpu_buffers: RenderMultidrawableBatchSetGpuBuffers,
447
448    /// A mapping from the phase item's bin key to the index of the
449    /// corresponding bin.
450    pub(crate) bin_key_to_bin_index: HashMap<BPI::BinKey, RenderBinIndex>,
451
452    /// The actual entities within each bin, indexed by [`RenderBinIndex`].
453    ///
454    /// This list isn't tightly packed.
455    bins: Vec<Option<RenderMultidrawableBin>>,
456
457    /// A list of unused [`RenderBinIndex`]es waiting to be reused.
458    ///
459    /// Each [`RenderBinIndex`] in this list corresponds to an empty bin.
460    bin_free_list: Vec<RenderBinIndex>,
461
462    /// A mapping from the indirect parameters offset to the index of each bin.
463    ///
464    /// The *indirect parameters offset* is the index of the GPU indirect draw
465    /// command for the bin, relative to the first such index for this batch
466    /// set.
467    indirect_parameters_offset_to_bin_index: Vec<RenderBinIndex>,
468
469    /// Information about each binned mesh instance kept on CPU.
470    pub(crate) render_binned_mesh_instances_cpu: Vec<CpuRenderBinnedMeshInstance>,
471}
472
473impl<BPI> RenderMultidrawableBatchSet<BPI>
474where
475    BPI: BinnedPhaseItem,
476{
477    /// Creates a new [`RenderMultidrawableBatchSet`] containing an empty set of
478    /// bins.
479    fn new() -> RenderMultidrawableBatchSet<BPI> {
480        RenderMultidrawableBatchSet {
481            gpu_buffers: RenderMultidrawableBatchSetGpuBuffers::new(),
482            bin_key_to_bin_index: HashMap::default(),
483            bins: ::alloc::vec::Vec::new()vec![],
484            bin_free_list: ::alloc::vec::Vec::new()vec![],
485            indirect_parameters_offset_to_bin_index: ::alloc::vec::Vec::new()vec![],
486            render_binned_mesh_instances_cpu: ::alloc::vec::Vec::new()vec![],
487        }
488    }
489
490    /// Returns the first entity in the first bin (if there is one).
491    pub(crate) fn representative_entity(&self) -> Option<MainEntity> {
492        let first_bin_index = self.bin_key_to_bin_index.values().next()?;
493        let first_bin = self.bin(*first_bin_index).expect("Bin should be present");
494        first_bin
495            .entity_to_binned_mesh_instance_index
496            .keys()
497            .next()
498            .copied()
499    }
500
501    /// Returns the [`RenderMultidrawableBin`] for the given [`RenderBinIndex`].
502    pub(crate) fn bin(&self, bin_index: RenderBinIndex) -> Option<&RenderMultidrawableBin> {
503        self.bins
504            .get(bin_index.0 as usize)
505            .and_then(|bin| bin.as_ref())
506    }
507
508    /// Inserts an entity with the given uniform index into the bin with the
509    /// given key.
510    fn insert(
511        &mut self,
512        bin_key: BPI::BinKey,
513        main_entity: MainEntity,
514        input_uniform_index: InputUniformIndex,
515    ) {
516        let bin_index;
517        match self.bin_key_to_bin_index.entry(bin_key) {
518            Entry::Occupied(occupied_entry) => {
519                bin_index = *occupied_entry.get();
520            }
521            Entry::Vacant(vacant_entry) => {
522                // Create a bin. First, allocate a bin index.
523                bin_index = self
524                    .bin_free_list
525                    .pop()
526                    .unwrap_or(RenderBinIndex(self.bins.len() as u32));
527
528                // Initialize the bin at that index.
529                if bin_index.0 as usize == self.bins.len() {
530                    self.bins.push(Some(RenderMultidrawableBin::new()));
531                } else {
532                    if true {
    if !self.bins[bin_index.0 as usize].is_none() {
        ::core::panicking::panic("assertion failed: self.bins[bin_index.0 as usize].is_none()")
    };
};debug_assert!(self.bins[bin_index.0 as usize].is_none());
533                    self.bins[bin_index.0 as usize] = Some(RenderMultidrawableBin::new());
534                }
535                vacant_entry.insert(bin_index);
536
537                // Grab an indirect parameters offset.
538                self.allocate_indirect_parameters(bin_index);
539            }
540        }
541
542        // Update the GPU buffers.
543        let bin = self.bins[bin_index.0 as usize].as_mut().unwrap();
544        self.gpu_buffers.insert(
545            bin,
546            &mut self.render_binned_mesh_instances_cpu,
547            main_entity,
548            input_uniform_index,
549            bin_index,
550        );
551    }
552
553    /// Removes the given entity from the bin with the given key.
554    ///
555    /// The given entity must be present in that bin.
556    fn remove(&mut self, main_entity: MainEntity, bin_key: &BPI::BinKey) {
557        // Fetch the bin index.
558        let bin_index = *self
559            .bin_key_to_bin_index
560            .get(bin_key)
561            .expect("Bin key not present");
562        let bin = self.bins[bin_index.0 as usize].as_mut().unwrap();
563
564        let maybe_displaced_entity_indices =
565            self.gpu_buffers
566                .remove(bin, &mut self.render_binned_mesh_instances_cpu, main_entity);
567        if let Some((old_render_bin_buffer_index, displaced_entity_indices)) =
568            maybe_displaced_entity_indices
569        {
570            self.bins[displaced_entity_indices.bin_index.0 as usize]
571                .as_mut()
572                .expect("Bin not present")
573                .entity_to_binned_mesh_instance_index
574                .insert(
575                    displaced_entity_indices.main_entity,
576                    old_render_bin_buffer_index,
577                );
578        }
579
580        self.remove_bin_if_empty(bin_key, bin_index);
581    }
582
583    /// Allocates an indirect parameters slot for a new bin.
584    fn allocate_indirect_parameters(&mut self, bin_index: RenderBinIndex) {
585        // Indirect parameters must be tightly packed, so we always add one to
586        // the end of the list. Record the bin index for the new indirect
587        // parameters offset.
588        let indirect_parameters_offset = self.indirect_parameters_offset_to_bin_index.len() as u32;
589        self.indirect_parameters_offset_to_bin_index.push(bin_index);
590
591        // Update the reverse mapping from bin index to indirect parameters offset.
592        if bin_index.0 as usize
593            == self
594                .gpu_buffers
595                .bin_index_to_indirect_parameters_offset_buffer
596                .len()
597        {
598            self.gpu_buffers
599                .bin_index_to_indirect_parameters_offset_buffer
600                .push(indirect_parameters_offset);
601        } else {
602            self.gpu_buffers
603                .bin_index_to_indirect_parameters_offset_buffer
604                .values_mut()[bin_index.0 as usize] = indirect_parameters_offset;
605        }
606    }
607
608    /// A helper method that removes a bin if it just became empty.
609    fn remove_bin_if_empty(&mut self, bin_key: &BPI::BinKey, bin_index: RenderBinIndex) {
610        // Is the bin empty? If not, bail.
611        let bin = self.bins[bin_index.0 as usize].as_mut().unwrap();
612        if !bin.is_empty() {
613            return;
614        }
615
616        // Remove the bin.
617        self.bin_key_to_bin_index.remove(bin_key);
618        self.bin_free_list.push(bin_index);
619        self.bins[bin_index.0 as usize] = None;
620
621        // Remove the indirect parameters offset corresponding to the bin. Note
622        // that indirect parameters must be tightly packed. Thus we must use
623        // `swap_remove`.
624        let indirect_parameters_offset = mem::replace(
625            &mut self
626                .gpu_buffers
627                .bin_index_to_indirect_parameters_offset_buffer
628                .values_mut()[bin_index.0 as usize],
629            u32::MAX,
630        );
631        let removed_bin_index = self
632            .indirect_parameters_offset_to_bin_index
633            .swap_remove(indirect_parameters_offset as usize);
634        if true {
    match (&bin_index, &removed_bin_index) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(bin_index, removed_bin_index);
635
636        // `swap_remove` may have changed the indirect parameter index of some
637        // other bin (specifically, the one that was previously at the end of
638        // the `Self::indirect_parameters_offset_to_bin_index` list). If it did,
639        // then we need to update the
640        // `Self::bin_index_to_indirect_parameters_offset_buffer` table to
641        // reflect the new offset of that displaced bin.
642        if let Some(displaced_bin_index) = self
643            .indirect_parameters_offset_to_bin_index
644            .get(indirect_parameters_offset as usize)
645        {
646            self.gpu_buffers
647                .bin_index_to_indirect_parameters_offset_buffer
648                .set(displaced_bin_index.0, indirect_parameters_offset);
649        }
650    }
651
652    fn is_empty(&self) -> bool {
653        self.bin_free_list.len() == self.bins.len()
654    }
655
656    pub(crate) fn bin_count(&self) -> usize {
657        self.bin_key_to_bin_index.len()
658    }
659}
660
661/// A single mesh instance in a bin.
662///
663/// This is a data structure shared between CPU and GPU. It is *not* sorted in
664/// the [`RenderMultidrawableBatchSetGpuBuffers`]: mesh instances in any given
665/// bin are not guaranteed to be adjacent.
666#[derive(#[automatically_derived]
impl ::core::clone::Clone for GpuRenderBinnedMeshInstance {
    #[inline]
    fn clone(&self) -> GpuRenderBinnedMeshInstance {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for GpuRenderBinnedMeshInstance { }Copy, #[automatically_derived]
impl ::core::default::Default for GpuRenderBinnedMeshInstance {
    #[inline]
    fn default() -> GpuRenderBinnedMeshInstance {
        GpuRenderBinnedMeshInstance {
            input_uniform_index: ::core::default::Default::default(),
            bin_index: ::core::default::Default::default(),
        }
    }
}Default, unsafe impl ::bytemuck::Pod for GpuRenderBinnedMeshInstance {}Pod, unsafe impl ::bytemuck::Zeroable for GpuRenderBinnedMeshInstance {}Zeroable, impl ::encase::private::ShaderSize for GpuRenderBinnedMeshInstance where
    u32: ::encase::private::ShaderSize, u32: ::encase::private::ShaderSize {}ShaderType)]
667#[repr(C)]
668pub struct GpuRenderBinnedMeshInstance {
669    /// The index of the `MeshInputUniform` in the buffer.
670    ///
671    /// This should be an [`InputUniformIndex`], but `encase` doesn't support
672    /// newtype structs.
673    pub(crate) input_uniform_index: u32,
674
675    /// The index of the bin in this batch set.
676    ///
677    /// This is the index tracked by [`RenderMultidrawableBatchSet::bins`].
678    ///
679    /// This should be a [`RenderBinIndex`], but `encase` doesn't support that
680    bin_index: u32,
681}
682
683/// Information that we keep about an entity currently within a bin.
684pub struct CachedBinnedEntity<BPI>
685where
686    BPI: BinnedPhaseItem,
687{
688    /// Information that we use to identify a cached entity in a bin.
689    pub cached_bin_key: Option<CachedBinKey<BPI>>,
690}
691
692/// Information that we use to identify a cached entity in a bin.
693pub struct CachedBinKey<BPI>
694where
695    BPI: BinnedPhaseItem,
696{
697    /// The key of the batch set containing the entity.
698    pub batch_set_key: BPI::BatchSetKey,
699    /// The key of the bin containing the entity.
700    pub bin_key: BPI::BinKey,
701    /// The type of render phase that we use to render the entity: multidraw,
702    /// plain batch, etc.
703    pub phase_type: BinnedRenderPhaseType,
704}
705
706impl<BPI> Clone for CachedBinnedEntity<BPI>
707where
708    BPI: BinnedPhaseItem,
709{
710    fn clone(&self) -> Self {
711        CachedBinnedEntity {
712            cached_bin_key: self.cached_bin_key.clone(),
713        }
714    }
715}
716
717impl<BPI> Clone for CachedBinKey<BPI>
718where
719    BPI: BinnedPhaseItem,
720{
721    fn clone(&self) -> Self {
722        CachedBinKey {
723            batch_set_key: self.batch_set_key.clone(),
724            bin_key: self.bin_key.clone(),
725            phase_type: self.phase_type,
726        }
727    }
728}
729
730impl<BPI> PartialEq for CachedBinKey<BPI>
731where
732    BPI: BinnedPhaseItem,
733{
734    fn eq(&self, other: &Self) -> bool {
735        self.batch_set_key == other.batch_set_key
736            && self.bin_key == other.bin_key
737            && self.phase_type == other.phase_type
738    }
739}
740
741/// How we store and render the batch sets.
742///
743/// Each one of these corresponds to a [`GpuPreprocessingMode`].
744pub enum BinnedRenderPhaseBatchSets<BK> {
745    /// Batches are grouped into batch sets based on dynamic uniforms.
746    ///
747    /// This corresponds to [`GpuPreprocessingMode::None`].
748    DynamicUniforms(Vec<SmallVec<[BinnedRenderPhaseBatch; 1]>>),
749
750    /// Batches are never grouped into batch sets.
751    ///
752    /// This corresponds to [`GpuPreprocessingMode::PreprocessingOnly`].
753    Direct(Vec<BinnedRenderPhaseBatch>),
754
755    /// Batches are grouped together into batch sets based on their ability to
756    /// be multi-drawn together.
757    ///
758    /// This corresponds to [`GpuPreprocessingMode::Culling`].
759    MultidrawIndirect(Vec<BinnedRenderPhaseBatchSet<BK>>),
760}
761
762/// A group of entities that will be batched together into a single multi-draw
763/// call.
764pub struct BinnedRenderPhaseBatchSet<BK> {
765    /// The first batch in this batch set.
766    pub(crate) first_batch: BinnedRenderPhaseBatch,
767    /// The key of the bin that the first batch corresponds to.
768    pub(crate) bin_key: BK,
769    /// The number of batches.
770    pub(crate) batch_count: u32,
771    /// The index of the batch set in the GPU buffer.
772    pub(crate) index: u32,
773    /// The index of the first preprocessing work item for this batch set in the
774    /// preprocessing work item buffer.
775    pub(crate) first_work_item_index: u32,
776}
777
778impl<BK> BinnedRenderPhaseBatchSets<BK> {
779    fn clear(&mut self) {
780        match *self {
781            BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut vec) => vec.clear(),
782            BinnedRenderPhaseBatchSets::Direct(ref mut vec) => vec.clear(),
783            BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => vec.clear(),
784        }
785    }
786}
787
788/// Information about a single batch of entities rendered using binned phase
789/// items.
790#[derive(#[automatically_derived]
impl ::core::fmt::Debug for BinnedRenderPhaseBatch {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "BinnedRenderPhaseBatch", "representative_entity",
            &self.representative_entity, "instance_range",
            &self.instance_range, "extra_index", &&self.extra_index)
    }
}Debug)]
791pub struct BinnedRenderPhaseBatch {
792    /// An entity that's *representative* of this batch.
793    ///
794    /// Bevy uses this to fetch the mesh. It can be any entity in the batch.
795    pub representative_entity: (Entity, MainEntity),
796    /// The range of instance indices in this batch.
797    pub instance_range: Range<u32>,
798
799    /// The dynamic offset of the batch.
800    ///
801    /// Note that dynamic offsets are only used on platforms that don't support
802    /// storage buffers.
803    pub extra_index: PhaseItemExtraIndex,
804}
805
806/// Information about the unbatchable entities in a bin.
807pub struct UnbatchableBinnedEntities {
808    /// The entities.
809    pub entities: MainEntityHashMap<Entity>,
810
811    /// The GPU array buffer indices of each unbatchable binned entity.
812    pub(crate) buffer_indices: UnbatchableBinnedEntityIndexSet,
813}
814
815/// Information about [`BinnedRenderPhaseType::NonMesh`] entities.
816pub struct NonMeshEntities {
817    /// The entities.
818    pub entities: MainEntityHashMap<Entity>,
819}
820
821/// Stores instance indices and dynamic offsets for unbatchable entities in a
822/// binned render phase.
823///
824/// This is conceptually `Vec<UnbatchableBinnedEntityDynamicOffset>`, but it
825/// avoids the overhead of storing dynamic offsets on platforms that support
826/// them. In other words, this allows a fast path that avoids allocation on
827/// platforms that aren't WebGL 2.
828#[derive(#[automatically_derived]
impl ::core::default::Default for UnbatchableBinnedEntityIndexSet {
    #[inline]
    fn default() -> UnbatchableBinnedEntityIndexSet { Self::NoEntities }
}Default)]
829
830pub(crate) enum UnbatchableBinnedEntityIndexSet {
831    /// There are no unbatchable entities in this bin (yet).
832    #[default]
833    NoEntities,
834
835    /// The instances for all unbatchable entities in this bin are contiguous,
836    /// and there are no dynamic uniforms.
837    ///
838    /// This is the typical case on platforms other than WebGL 2. We special
839    /// case this to avoid allocation on those platforms.
840    Sparse {
841        /// The range of indices.
842        instance_range: Range<u32>,
843        /// The index of the first indirect instance parameters.
844        ///
845        /// The other indices immediately follow these.
846        first_indirect_parameters_index: Option<NonMaxU32>,
847    },
848
849    /// Dynamic uniforms are present for unbatchable entities in this bin.
850    ///
851    /// We fall back to this on WebGL 2.
852    Dense(Vec<UnbatchableBinnedEntityIndices>),
853}
854
855/// The instance index and dynamic offset (if present) for an unbatchable entity.
856///
857/// This is only useful on platforms that don't support storage buffers.
858#[derive(#[automatically_derived]
impl ::core::clone::Clone for UnbatchableBinnedEntityIndices {
    #[inline]
    fn clone(&self) -> UnbatchableBinnedEntityIndices {
        UnbatchableBinnedEntityIndices {
            instance_index: ::core::clone::Clone::clone(&self.instance_index),
            extra_index: ::core::clone::Clone::clone(&self.extra_index),
        }
    }
}Clone)]
859pub(crate) struct UnbatchableBinnedEntityIndices {
860    /// The instance index.
861    pub(crate) instance_index: u32,
862    /// The [`PhaseItemExtraIndex`], if present.
863    pub(crate) extra_index: PhaseItemExtraIndex,
864}
865
866/// Identifies the list within [`BinnedRenderPhase`] that a phase item is to be
867/// placed in.
868#[derive(#[automatically_derived]
impl ::core::clone::Clone for BinnedRenderPhaseType {
    #[inline]
    fn clone(&self) -> BinnedRenderPhaseType { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BinnedRenderPhaseType { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for BinnedRenderPhaseType {
    #[inline]
    fn eq(&self, other: &BinnedRenderPhaseType) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for BinnedRenderPhaseType {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                BinnedRenderPhaseType::MultidrawableMesh =>
                    "MultidrawableMesh",
                BinnedRenderPhaseType::BatchableMesh => "BatchableMesh",
                BinnedRenderPhaseType::UnbatchableMesh => "UnbatchableMesh",
                BinnedRenderPhaseType::NonMesh => "NonMesh",
            })
    }
}Debug)]
869pub enum BinnedRenderPhaseType {
870    /// The item is a mesh that's eligible for multi-draw indirect rendering and
871    /// can be batched with other meshes of the same type.
872    MultidrawableMesh,
873
874    /// The item is a mesh that can be batched with other meshes of the same type and
875    /// drawn in a single draw call.
876    BatchableMesh,
877
878    /// The item is a mesh that's eligible for indirect rendering, but can't be
879    /// batched with other meshes of the same type.
880    UnbatchableMesh,
881
882    /// The item isn't a mesh at all.
883    ///
884    /// Bevy will simply invoke the drawing commands for such items one after
885    /// another, with no further processing.
886    ///
887    /// The engine itself doesn't enqueue any items of this type, but it's
888    /// available for use in your application and/or plugins.
889    NonMesh,
890}
891
892impl<T> From<GpuArrayBufferIndex<T>> for UnbatchableBinnedEntityIndices
893where
894    T: Clone + ShaderSize + WriteInto,
895{
896    fn from(value: GpuArrayBufferIndex<T>) -> Self {
897        UnbatchableBinnedEntityIndices {
898            instance_index: value.index,
899            extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(value.dynamic_offset),
900        }
901    }
902}
903
904impl<BPI> Default for ViewBinnedRenderPhases<BPI>
905where
906    BPI: BinnedPhaseItem,
907{
908    fn default() -> Self {
909        Self(default())
910    }
911}
912
913impl<BPI> ViewBinnedRenderPhases<BPI>
914where
915    BPI: BinnedPhaseItem,
916{
917    pub fn prepare_for_new_frame(
918        &mut self,
919        retained_view_entity: RetainedViewEntity,
920        gpu_preprocessing: GpuPreprocessingMode,
921    ) {
922        match self.entry(retained_view_entity) {
923            Entry::Occupied(mut entry) => entry.get_mut().prepare_for_new_frame(),
924            Entry::Vacant(entry) => {
925                entry.insert(BinnedRenderPhase::<BPI>::new(gpu_preprocessing));
926            }
927        }
928    }
929}
930
931/// The index of the uniform describing this object in the GPU buffer, when GPU
932/// preprocessing is enabled.
933///
934/// For example, for 3D meshes, this is the index of the `MeshInputUniform` in
935/// the buffer.
936///
937/// This field is ignored if GPU preprocessing isn't in use, such as (currently)
938/// in the case of 2D meshes. In that case, it can be safely set to
939/// [`core::default::Default::default`].
940#[derive(#[automatically_derived]
impl ::core::clone::Clone for InputUniformIndex {
    #[inline]
    fn clone(&self) -> InputUniformIndex {
        let _: ::core::clone::AssertParamIsClone<u32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for InputUniformIndex { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for InputUniformIndex {
    #[inline]
    fn eq(&self, other: &InputUniformIndex) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::default::Default for InputUniformIndex {
    #[inline]
    fn default() -> InputUniformIndex {
        InputUniformIndex(::core::default::Default::default())
    }
}Default, impl ::core::ops::Deref for InputUniformIndex {
    type Target = u32;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for InputUniformIndex {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut, #[automatically_derived]
impl ::core::fmt::Debug for InputUniformIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "InputUniformIndex", &&self.0)
    }
}Debug, unsafe impl ::bytemuck::Pod for InputUniformIndex {}Pod, unsafe impl ::bytemuck::Zeroable for InputUniformIndex {}Zeroable)]
941#[repr(transparent)]
942pub struct InputUniformIndex(pub u32);
943
944impl<BPI> BinnedRenderPhase<BPI>
945where
946    BPI: BinnedPhaseItem,
947{
948    /// Bins a new entity.
949    ///
950    /// The `phase_type` parameter specifies whether the entity is a
951    /// preprocessable mesh and whether it can be binned with meshes of the same
952    /// type.
953    pub fn add(
954        &mut self,
955        batch_set_key: BPI::BatchSetKey,
956        bin_key: BPI::BinKey,
957        (entity, main_entity): (Entity, MainEntity),
958        input_uniform_index: InputUniformIndex,
959        mut phase_type: BinnedRenderPhaseType,
960    ) {
961        // If the user has overridden indirect drawing for this view, we need to
962        // force the phase type to be batchable instead.
963        if self.gpu_preprocessing_mode == GpuPreprocessingMode::PreprocessingOnly
964            && phase_type == BinnedRenderPhaseType::MultidrawableMesh
965        {
966            phase_type = BinnedRenderPhaseType::BatchableMesh;
967        }
968
969        match phase_type {
970            BinnedRenderPhaseType::MultidrawableMesh => {
971                match self.multidrawable_meshes.entry(batch_set_key.clone()) {
972                    indexmap::map::Entry::Occupied(mut entry) => {
973                        entry
974                            .get_mut()
975                            .insert(bin_key.clone(), main_entity, input_uniform_index);
976                    }
977                    indexmap::map::Entry::Vacant(entry) => {
978                        let mut new_batch_set = RenderMultidrawableBatchSet::new();
979                        new_batch_set.insert(bin_key.clone(), main_entity, input_uniform_index);
980                        entry.insert(new_batch_set);
981                    }
982                }
983            }
984
985            BinnedRenderPhaseType::BatchableMesh => {
986                match self
987                    .batchable_meshes
988                    .entry((batch_set_key.clone(), bin_key.clone()).clone())
989                {
990                    indexmap::map::Entry::Occupied(mut entry) => {
991                        entry.get_mut().insert(main_entity, input_uniform_index);
992                    }
993                    indexmap::map::Entry::Vacant(entry) => {
994                        entry.insert(RenderBin::from_entity(main_entity, input_uniform_index));
995                    }
996                }
997            }
998
999            BinnedRenderPhaseType::UnbatchableMesh => {
1000                match self
1001                    .unbatchable_meshes
1002                    .entry((batch_set_key.clone(), bin_key.clone()))
1003                {
1004                    indexmap::map::Entry::Occupied(mut entry) => {
1005                        entry.get_mut().entities.insert(main_entity, entity);
1006                    }
1007                    indexmap::map::Entry::Vacant(entry) => {
1008                        let mut entities = MainEntityHashMap::default();
1009                        entities.insert(main_entity, entity);
1010                        entry.insert(UnbatchableBinnedEntities {
1011                            entities,
1012                            buffer_indices: default(),
1013                        });
1014                    }
1015                }
1016            }
1017
1018            BinnedRenderPhaseType::NonMesh => {
1019                // We don't process these items further.
1020                match self
1021                    .non_mesh_items
1022                    .entry((batch_set_key.clone(), bin_key.clone()).clone())
1023                {
1024                    indexmap::map::Entry::Occupied(mut entry) => {
1025                        entry.get_mut().entities.insert(main_entity, entity);
1026                    }
1027                    indexmap::map::Entry::Vacant(entry) => {
1028                        let mut entities = MainEntityHashMap::default();
1029                        entities.insert(main_entity, entity);
1030                        entry.insert(NonMeshEntities { entities });
1031                    }
1032                }
1033            }
1034        }
1035
1036        // Update the cache.
1037        self.update_cache(
1038            main_entity,
1039            Some(CachedBinKey {
1040                batch_set_key,
1041                bin_key,
1042                phase_type,
1043            }),
1044        );
1045    }
1046
1047    /// Inserts an entity into the cache with the given change tick.
1048    pub fn update_cache(
1049        &mut self,
1050        main_entity: MainEntity,
1051        cached_bin_key: Option<CachedBinKey<BPI>>,
1052    ) {
1053        let new_cached_binned_entity = CachedBinnedEntity { cached_bin_key };
1054        self.cached_entity_bin_keys
1055            .insert(main_entity, new_cached_binned_entity);
1056    }
1057
1058    /// Encodes the GPU commands needed to render all entities in this phase.
1059    pub fn render<'w>(
1060        &self,
1061        render_pass: &mut TrackedRenderPass<'w>,
1062        world: &'w World,
1063        view: Entity,
1064    ) -> Result<(), DrawError> {
1065        {
1066            let draw_functions = world.resource::<DrawFunctions<BPI>>();
1067            let mut draw_functions = draw_functions.write();
1068            draw_functions.prepare(world);
1069            // Make sure to drop the reader-writer lock here to avoid recursive
1070            // locks.
1071        }
1072
1073        self.render_batchable_meshes(render_pass, world, view)?;
1074        self.render_unbatchable_meshes(render_pass, world, view)?;
1075        self.render_non_meshes(render_pass, world, view)?;
1076
1077        Ok(())
1078    }
1079
1080    /// Renders all batchable meshes queued in this phase.
1081    fn render_batchable_meshes<'w>(
1082        &self,
1083        render_pass: &mut TrackedRenderPass<'w>,
1084        world: &'w World,
1085        view: Entity,
1086    ) -> Result<(), DrawError> {
1087        let draw_functions = world.resource::<DrawFunctions<BPI>>();
1088        let mut draw_functions = draw_functions.write();
1089
1090        let render_device = world.resource::<RenderDevice>();
1091        let render_adapter_info = world.resource::<RenderAdapterInfo>();
1092        let multi_draw_indirect_count_supported = render_device
1093            .features()
1094            .contains(Features::MULTI_DRAW_INDIRECT_COUNT)
1095            // TODO: https://github.com/gfx-rs/wgpu/issues/7974
1096            && !#[allow(non_exhaustive_omitted_patterns)] match render_adapter_info.backend {
    wgpu::Backend::Dx12 => true,
    _ => false,
}matches!(render_adapter_info.backend, wgpu::Backend::Dx12);
1097
1098        match self.batch_sets {
1099            BinnedRenderPhaseBatchSets::DynamicUniforms(ref batch_sets) => {
1100                if true {
    match (&self.batchable_meshes.len(), &batch_sets.len()) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    };
};debug_assert_eq!(self.batchable_meshes.len(), batch_sets.len());
1101
1102                for ((batch_set_key, bin_key), batch_set) in
1103                    self.batchable_meshes.keys().zip(batch_sets.iter())
1104                {
1105                    for batch in batch_set {
1106                        let binned_phase_item = BPI::new(
1107                            batch_set_key.clone(),
1108                            bin_key.clone(),
1109                            batch.representative_entity,
1110                            batch.instance_range.clone(),
1111                            batch.extra_index.clone(),
1112                        );
1113
1114                        // Fetch the draw function.
1115                        let Some(draw_function) =
1116                            draw_functions.get_mut(binned_phase_item.draw_function())
1117                        else {
1118                            continue;
1119                        };
1120
1121                        draw_function.draw(world, render_pass, view, &binned_phase_item)?;
1122                    }
1123                }
1124            }
1125
1126            BinnedRenderPhaseBatchSets::Direct(ref batch_set) => {
1127                for (batch, (batch_set_key, bin_key)) in
1128                    batch_set.iter().zip(self.batchable_meshes.keys())
1129                {
1130                    let binned_phase_item = BPI::new(
1131                        batch_set_key.clone(),
1132                        bin_key.clone(),
1133                        batch.representative_entity,
1134                        batch.instance_range.clone(),
1135                        batch.extra_index.clone(),
1136                    );
1137
1138                    // Fetch the draw function.
1139                    let Some(draw_function) =
1140                        draw_functions.get_mut(binned_phase_item.draw_function())
1141                    else {
1142                        continue;
1143                    };
1144
1145                    draw_function.draw(world, render_pass, view, &binned_phase_item)?;
1146                }
1147            }
1148
1149            BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) => {
1150                for (batch_set_key, batch_set) in self
1151                    .multidrawable_meshes
1152                    .keys()
1153                    .chain(
1154                        self.batchable_meshes
1155                            .keys()
1156                            .map(|(batch_set_key, _)| batch_set_key),
1157                    )
1158                    .zip(batch_sets.iter())
1159                {
1160                    let batch = &batch_set.first_batch;
1161
1162                    let batch_set_index = if multi_draw_indirect_count_supported {
1163                        NonMaxU32::new(batch_set.index)
1164                    } else {
1165                        None
1166                    };
1167
1168                    let binned_phase_item = BPI::new(
1169                        batch_set_key.clone(),
1170                        batch_set.bin_key.clone(),
1171                        batch.representative_entity,
1172                        batch.instance_range.clone(),
1173                        match batch.extra_index {
1174                            PhaseItemExtraIndex::None => PhaseItemExtraIndex::None,
1175                            PhaseItemExtraIndex::DynamicOffset(ref dynamic_offset) => {
1176                                PhaseItemExtraIndex::DynamicOffset(*dynamic_offset)
1177                            }
1178                            PhaseItemExtraIndex::IndirectParametersIndex { ref range, .. } => {
1179                                PhaseItemExtraIndex::IndirectParametersIndex {
1180                                    range: range.start..(range.start + batch_set.batch_count),
1181                                    batch_set_index,
1182                                }
1183                            }
1184                        },
1185                    );
1186
1187                    // Fetch the draw function.
1188                    let Some(draw_function) =
1189                        draw_functions.get_mut(binned_phase_item.draw_function())
1190                    else {
1191                        continue;
1192                    };
1193
1194                    draw_function.draw(world, render_pass, view, &binned_phase_item)?;
1195                }
1196            }
1197        }
1198
1199        Ok(())
1200    }
1201
1202    /// Renders all unbatchable meshes queued in this phase.
1203    fn render_unbatchable_meshes<'w>(
1204        &self,
1205        render_pass: &mut TrackedRenderPass<'w>,
1206        world: &'w World,
1207        view: Entity,
1208    ) -> Result<(), DrawError> {
1209        let draw_functions = world.resource::<DrawFunctions<BPI>>();
1210        let mut draw_functions = draw_functions.write();
1211
1212        for (batch_set_key, bin_key) in self.unbatchable_meshes.keys() {
1213            let unbatchable_entities =
1214                &self.unbatchable_meshes[&(batch_set_key.clone(), bin_key.clone())];
1215            for (entity_index, entity) in unbatchable_entities.entities.iter().enumerate() {
1216                let unbatchable_dynamic_offset = match &unbatchable_entities.buffer_indices {
1217                    UnbatchableBinnedEntityIndexSet::NoEntities => {
1218                        // Shouldn't happen…
1219                        continue;
1220                    }
1221                    UnbatchableBinnedEntityIndexSet::Sparse {
1222                        instance_range,
1223                        first_indirect_parameters_index,
1224                    } => UnbatchableBinnedEntityIndices {
1225                        instance_index: instance_range.start + entity_index as u32,
1226                        extra_index: match first_indirect_parameters_index {
1227                            None => PhaseItemExtraIndex::None,
1228                            Some(first_indirect_parameters_index) => {
1229                                let first_indirect_parameters_index_for_entity =
1230                                    u32::from(*first_indirect_parameters_index)
1231                                        + entity_index as u32;
1232                                PhaseItemExtraIndex::IndirectParametersIndex {
1233                                    range: first_indirect_parameters_index_for_entity
1234                                        ..(first_indirect_parameters_index_for_entity + 1),
1235                                    batch_set_index: None,
1236                                }
1237                            }
1238                        },
1239                    },
1240                    UnbatchableBinnedEntityIndexSet::Dense(dynamic_offsets) => {
1241                        dynamic_offsets[entity_index].clone()
1242                    }
1243                };
1244
1245                let binned_phase_item = BPI::new(
1246                    batch_set_key.clone(),
1247                    bin_key.clone(),
1248                    (*entity.1, *entity.0),
1249                    unbatchable_dynamic_offset.instance_index
1250                        ..(unbatchable_dynamic_offset.instance_index + 1),
1251                    unbatchable_dynamic_offset.extra_index,
1252                );
1253
1254                // Fetch the draw function.
1255                let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
1256                else {
1257                    continue;
1258                };
1259
1260                draw_function.draw(world, render_pass, view, &binned_phase_item)?;
1261            }
1262        }
1263        Ok(())
1264    }
1265
1266    /// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`].
1267    ///
1268    /// These will have been added by plugins or the application.
1269    fn render_non_meshes<'w>(
1270        &self,
1271        render_pass: &mut TrackedRenderPass<'w>,
1272        world: &'w World,
1273        view: Entity,
1274    ) -> Result<(), DrawError> {
1275        let draw_functions = world.resource::<DrawFunctions<BPI>>();
1276        let mut draw_functions = draw_functions.write();
1277
1278        for ((batch_set_key, bin_key), non_mesh_entities) in &self.non_mesh_items {
1279            for (main_entity, entity) in non_mesh_entities.entities.iter() {
1280                // Come up with a fake batch range and extra index. The draw
1281                // function is expected to manage any sort of batching logic itself.
1282                let binned_phase_item = BPI::new(
1283                    batch_set_key.clone(),
1284                    bin_key.clone(),
1285                    (*entity, *main_entity),
1286                    0..1,
1287                    PhaseItemExtraIndex::None,
1288                );
1289
1290                let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
1291                else {
1292                    continue;
1293                };
1294
1295                draw_function.draw(world, render_pass, view, &binned_phase_item)?;
1296            }
1297        }
1298
1299        Ok(())
1300    }
1301
1302    pub fn is_empty(&self) -> bool {
1303        self.multidrawable_meshes.is_empty()
1304            && self.batchable_meshes.is_empty()
1305            && self.unbatchable_meshes.is_empty()
1306            && self.non_mesh_items.is_empty()
1307    }
1308
1309    pub fn prepare_for_new_frame(&mut self) {
1310        self.batch_sets.clear();
1311
1312        for unbatchable_bin in self.unbatchable_meshes.values_mut() {
1313            unbatchable_bin.buffer_indices.clear();
1314        }
1315    }
1316
1317    /// Removes a single entity from its bin.
1318    ///
1319    /// If doing so makes the bin empty, this method removes the bin as well.
1320    pub fn remove(&mut self, main_entity: MainEntity) {
1321        let Some(cached_binned_entity) = self.cached_entity_bin_keys.remove(&main_entity) else {
1322            return;
1323        };
1324
1325        if let Some(ref cached_bin_key) = cached_binned_entity.cached_bin_key {
1326            remove_entity_from_bin(
1327                main_entity,
1328                cached_bin_key,
1329                &mut self.multidrawable_meshes,
1330                &mut self.batchable_meshes,
1331                &mut self.unbatchable_meshes,
1332                &mut self.non_mesh_items,
1333            );
1334        }
1335    }
1336}
1337
1338/// Removes an entity from a bin.
1339///
1340/// If this makes the bin empty, this function removes the bin as well.
1341///
1342/// This is a standalone function instead of a method on [`BinnedRenderPhase`]
1343/// for borrow check reasons.
1344fn remove_entity_from_bin<BPI>(
1345    entity: MainEntity,
1346    entity_bin_key: &CachedBinKey<BPI>,
1347    multidrawable_meshes: &mut IndexMap<BPI::BatchSetKey, RenderMultidrawableBatchSet<BPI>>,
1348    batchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
1349    unbatchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
1350    non_mesh_items: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
1351) where
1352    BPI: BinnedPhaseItem,
1353{
1354    match entity_bin_key.phase_type {
1355        BinnedRenderPhaseType::MultidrawableMesh => {
1356            if let indexmap::map::Entry::Occupied(mut batch_set_entry) =
1357                multidrawable_meshes.entry(entity_bin_key.batch_set_key.clone())
1358            {
1359                batch_set_entry
1360                    .get_mut()
1361                    .remove(entity, &entity_bin_key.bin_key);
1362
1363                // If the batch set is now empty, remove it. This will perturb
1364                // the order, but that's OK because we're going to sort the bin
1365                // afterwards.
1366                if batch_set_entry.get_mut().is_empty() {
1367                    batch_set_entry.swap_remove();
1368                }
1369            }
1370        }
1371
1372        BinnedRenderPhaseType::BatchableMesh => {
1373            if let indexmap::map::Entry::Occupied(mut bin_entry) = batchable_meshes.entry((
1374                entity_bin_key.batch_set_key.clone(),
1375                entity_bin_key.bin_key.clone(),
1376            )) {
1377                bin_entry.get_mut().remove(entity);
1378
1379                // If the bin is now empty, remove the bin.
1380                if bin_entry.get_mut().is_empty() {
1381                    bin_entry.swap_remove();
1382                }
1383            }
1384        }
1385
1386        BinnedRenderPhaseType::UnbatchableMesh => {
1387            if let indexmap::map::Entry::Occupied(mut bin_entry) = unbatchable_meshes.entry((
1388                entity_bin_key.batch_set_key.clone(),
1389                entity_bin_key.bin_key.clone(),
1390            )) {
1391                bin_entry.get_mut().entities.remove(&entity);
1392
1393                // If the bin is now empty, remove the bin.
1394                if bin_entry.get_mut().entities.is_empty() {
1395                    bin_entry.swap_remove();
1396                }
1397            }
1398        }
1399
1400        BinnedRenderPhaseType::NonMesh => {
1401            if let indexmap::map::Entry::Occupied(mut bin_entry) = non_mesh_items.entry((
1402                entity_bin_key.batch_set_key.clone(),
1403                entity_bin_key.bin_key.clone(),
1404            )) {
1405                bin_entry.get_mut().entities.remove(&entity);
1406
1407                // If the bin is now empty, remove the bin.
1408                if bin_entry.get_mut().entities.is_empty() {
1409                    bin_entry.swap_remove();
1410                }
1411            }
1412        }
1413    }
1414}
1415
1416impl<BPI> BinnedRenderPhase<BPI>
1417where
1418    BPI: BinnedPhaseItem,
1419{
1420    fn new(gpu_preprocessing: GpuPreprocessingMode) -> Self {
1421        Self {
1422            multidrawable_meshes: IndexMap::default(),
1423            batchable_meshes: IndexMap::default(),
1424            unbatchable_meshes: IndexMap::default(),
1425            non_mesh_items: IndexMap::default(),
1426            batch_sets: match gpu_preprocessing {
1427                GpuPreprocessingMode::Culling => {
1428                    BinnedRenderPhaseBatchSets::MultidrawIndirect(::alloc::vec::Vec::new()vec![])
1429                }
1430                GpuPreprocessingMode::PreprocessingOnly => {
1431                    BinnedRenderPhaseBatchSets::Direct(::alloc::vec::Vec::new()vec![])
1432                }
1433                GpuPreprocessingMode::None => BinnedRenderPhaseBatchSets::DynamicUniforms(::alloc::vec::Vec::new()vec![]),
1434            },
1435            cached_entity_bin_keys: MainEntityHashMap::default(),
1436            gpu_preprocessing_mode: gpu_preprocessing,
1437        }
1438    }
1439}
1440
1441impl UnbatchableBinnedEntityIndexSet {
1442    /// Returns the [`UnbatchableBinnedEntityIndices`] for the given entity.
1443    fn indices_for_entity_index(
1444        &self,
1445        entity_index: u32,
1446    ) -> Option<UnbatchableBinnedEntityIndices> {
1447        match self {
1448            UnbatchableBinnedEntityIndexSet::NoEntities => None,
1449            UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. }
1450                if entity_index >= instance_range.len() as u32 =>
1451            {
1452                None
1453            }
1454            UnbatchableBinnedEntityIndexSet::Sparse {
1455                instance_range,
1456                first_indirect_parameters_index: None,
1457            } => Some(UnbatchableBinnedEntityIndices {
1458                instance_index: instance_range.start + entity_index,
1459                extra_index: PhaseItemExtraIndex::None,
1460            }),
1461            UnbatchableBinnedEntityIndexSet::Sparse {
1462                instance_range,
1463                first_indirect_parameters_index: Some(first_indirect_parameters_index),
1464            } => {
1465                let first_indirect_parameters_index_for_this_batch =
1466                    u32::from(*first_indirect_parameters_index) + entity_index;
1467                Some(UnbatchableBinnedEntityIndices {
1468                    instance_index: instance_range.start + entity_index,
1469                    extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1470                        range: first_indirect_parameters_index_for_this_batch
1471                            ..(first_indirect_parameters_index_for_this_batch + 1),
1472                        batch_set_index: None,
1473                    },
1474                })
1475            }
1476            UnbatchableBinnedEntityIndexSet::Dense(indices) => {
1477                indices.get(entity_index as usize).cloned()
1478            }
1479        }
1480    }
1481}
1482
1483/// A convenient abstraction for adding all the systems necessary for a binned
1484/// render phase to the render app.
1485///
1486/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1487/// 3D PBR meshes.
1488pub struct BinnedRenderPhasePlugin<BPI, GFBD>
1489where
1490    BPI: BinnedPhaseItem,
1491    GFBD: GetFullBatchData,
1492{
1493    /// Debugging flags that can optionally be set when constructing the renderer.
1494    pub debug_flags: RenderDebugFlags,
1495    phantom: PhantomData<(BPI, GFBD)>,
1496}
1497
1498impl<BPI, GFBD> BinnedRenderPhasePlugin<BPI, GFBD>
1499where
1500    BPI: BinnedPhaseItem,
1501    GFBD: GetFullBatchData,
1502{
1503    pub fn new(debug_flags: RenderDebugFlags) -> Self {
1504        Self {
1505            debug_flags,
1506            phantom: PhantomData,
1507        }
1508    }
1509}
1510
1511impl<BPI, GFBD> Plugin for BinnedRenderPhasePlugin<BPI, GFBD>
1512where
1513    BPI: BinnedPhaseItem,
1514    GFBD: GetFullBatchData + Sync + Send + 'static,
1515{
1516    fn build(&self, app: &mut App) {
1517        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1518            return;
1519        };
1520
1521        render_app
1522            .init_gpu_resource::<ViewBinnedRenderPhases<BPI>>()
1523            .allow_ambiguous_resource::<ViewBinnedRenderPhases<BPI>>()
1524            .init_gpu_resource::<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>()
1525            .init_gpu_resource::<PhaseIndirectParametersBuffers<BPI>>()
1526            .add_systems(
1527                Render,
1528                (
1529                    batching::sort_binned_render_phase::<BPI>.in_set(RenderSystems::PhaseSort),
1530                    (
1531                        no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1532                            .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1533                        gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1534                            .run_if(
1535                                resource_exists::<
1536                                    BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1537                                >,
1538                            ),
1539                    )
1540                        .in_set(RenderSystems::PrepareResourcesBatchPhases)
1541                        .ambiguous_with(RenderSystems::PrepareResourcesBatchPhases),
1542                    gpu_preprocessing::write_binned_instance_buffers::<BPI, GFBD>
1543                        .run_if(
1544                            resource_exists::<
1545                                BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1546                            >,
1547                        )
1548                        .in_set(RenderSystems::PrepareResourcesWritePhaseBuffers)
1549                        .ambiguous_with(RenderSystems::PrepareResourcesWritePhaseBuffers),
1550                    gpu_preprocessing::collect_buffers_for_phase::<BPI, GFBD>
1551                        .run_if(
1552                            resource_exists::<
1553                                BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1554                            >,
1555                        )
1556                        .in_set(RenderSystems::PrepareResourcesCollectPhaseBuffers),
1557                ),
1558            );
1559    }
1560}
1561
1562/// Stores the rendering instructions for a single phase that sorts items in all
1563/// views.
1564///
1565/// They're cleared out every frame, but storing them in a resource like this
1566/// allows us to reuse allocations.
1567#[derive(impl<SPI> bevy_ecs::resource::Resource for ViewSortedRenderPhases<SPI> where
    SPI: SortedPhaseItem, Self: ::core::marker::Send + ::core::marker::Sync +
    'static {}Resource, impl<SPI> ::core::ops::Deref for ViewSortedRenderPhases<SPI> where
    SPI: SortedPhaseItem {
    type Target = HashMap<RetainedViewEntity, SortedRenderPhase<SPI>>;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl<SPI> ::core::ops::DerefMut for ViewSortedRenderPhases<SPI> where
    SPI: SortedPhaseItem {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
1568pub struct ViewSortedRenderPhases<SPI>(pub HashMap<RetainedViewEntity, SortedRenderPhase<SPI>>)
1569where
1570    SPI: SortedPhaseItem;
1571
1572impl<SPI> Default for ViewSortedRenderPhases<SPI>
1573where
1574    SPI: SortedPhaseItem,
1575{
1576    fn default() -> Self {
1577        Self(default())
1578    }
1579}
1580
1581impl<SPI> ViewSortedRenderPhases<SPI>
1582where
1583    SPI: SortedPhaseItem,
1584{
1585    /// Ensures that a set of phases are present for the given
1586    /// [`RetainedViewEntity`].
1587    pub fn prepare_for_new_frame(&mut self, retained_view_entity: RetainedViewEntity) {
1588        match self.entry(retained_view_entity) {
1589            Entry::Occupied(mut entry) => {
1590                let render_phase = entry.get_mut();
1591                for (render_entity, main_entity) in render_phase.transient_items.drain(..) {
1592                    render_phase
1593                        .items
1594                        .swap_remove(&(render_entity, main_entity));
1595                }
1596            }
1597            Entry::Vacant(entry) => {
1598                entry.insert(default());
1599            }
1600        }
1601    }
1602}
1603
1604/// A convenient abstraction for adding all the systems necessary for a sorted
1605/// render phase to the render app.
1606///
1607/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1608/// 3D PBR meshes.
1609pub struct SortedRenderPhasePlugin<SPI, GFBD>
1610where
1611    SPI: SortedPhaseItem,
1612    GFBD: GetFullBatchData,
1613{
1614    /// Debugging flags that can optionally be set when constructing the renderer.
1615    pub debug_flags: RenderDebugFlags,
1616    phantom: PhantomData<(SPI, GFBD)>,
1617}
1618
1619impl<SPI, GFBD> SortedRenderPhasePlugin<SPI, GFBD>
1620where
1621    SPI: SortedPhaseItem,
1622    GFBD: GetFullBatchData,
1623{
1624    pub fn new(debug_flags: RenderDebugFlags) -> Self {
1625        Self {
1626            debug_flags,
1627            phantom: PhantomData,
1628        }
1629    }
1630}
1631
1632impl<SPI, GFBD> Plugin for SortedRenderPhasePlugin<SPI, GFBD>
1633where
1634    SPI: SortedPhaseItem + CachedRenderPipelinePhaseItem,
1635    GFBD: GetFullBatchData + Sync + Send + 'static,
1636{
1637    fn build(&self, app: &mut App) {
1638        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1639            return;
1640        };
1641
1642        render_app
1643            .init_gpu_resource::<ViewSortedRenderPhases<SPI>>()
1644            .allow_ambiguous_resource::<ViewSortedRenderPhases<SPI>>()
1645            .init_gpu_resource::<PhaseBatchedInstanceBuffers<SPI, GFBD::BufferData>>()
1646            .init_gpu_resource::<PhaseIndirectParametersBuffers<SPI>>()
1647            .add_systems(
1648                Render,
1649                (
1650                    (
1651                        no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1652                            .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1653                        gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1654                            .run_if(
1655                                resource_exists::<
1656                                    BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1657                                >,
1658                            ),
1659                    )
1660                        .in_set(RenderSystems::PrepareResourcesBatchPhases),
1661                    gpu_preprocessing::collect_buffers_for_phase::<SPI, GFBD>
1662                        .run_if(
1663                            resource_exists::<
1664                                BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1665                            >,
1666                        )
1667                        .in_set(RenderSystems::PrepareResourcesCollectPhaseBuffers),
1668                ),
1669            );
1670    }
1671}
1672
1673impl UnbatchableBinnedEntityIndexSet {
1674    /// Adds a new entity to the list of unbatchable binned entities.
1675    pub fn add(&mut self, indices: UnbatchableBinnedEntityIndices) {
1676        match self {
1677            UnbatchableBinnedEntityIndexSet::NoEntities => {
1678                match indices.extra_index {
1679                    PhaseItemExtraIndex::DynamicOffset(_) => {
1680                        // This is the first entity we've seen, and we don't have
1681                        // compute shaders. Initialize an array.
1682                        *self = UnbatchableBinnedEntityIndexSet::Dense(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [indices]))vec![indices]);
1683                    }
1684                    PhaseItemExtraIndex::None => {
1685                        // This is the first entity we've seen, and we have compute
1686                        // shaders. Initialize the fast path.
1687                        *self = UnbatchableBinnedEntityIndexSet::Sparse {
1688                            instance_range: indices.instance_index..indices.instance_index + 1,
1689                            first_indirect_parameters_index: None,
1690                        }
1691                    }
1692                    PhaseItemExtraIndex::IndirectParametersIndex {
1693                        range: ref indirect_parameters_index,
1694                        ..
1695                    } => {
1696                        // This is the first entity we've seen, and we have compute
1697                        // shaders. Initialize the fast path.
1698                        *self = UnbatchableBinnedEntityIndexSet::Sparse {
1699                            instance_range: indices.instance_index..indices.instance_index + 1,
1700                            first_indirect_parameters_index: NonMaxU32::new(
1701                                indirect_parameters_index.start,
1702                            ),
1703                        }
1704                    }
1705                }
1706            }
1707
1708            UnbatchableBinnedEntityIndexSet::Sparse {
1709                instance_range,
1710                first_indirect_parameters_index,
1711            } if instance_range.end == indices.instance_index
1712                && ((first_indirect_parameters_index.is_none()
1713                    && indices.extra_index == PhaseItemExtraIndex::None)
1714                    || first_indirect_parameters_index.is_some_and(
1715                        |first_indirect_parameters_index| match indices.extra_index {
1716                            PhaseItemExtraIndex::IndirectParametersIndex {
1717                                range: ref this_range,
1718                                ..
1719                            } => {
1720                                u32::from(first_indirect_parameters_index) + instance_range.end
1721                                    - instance_range.start
1722                                    == this_range.start
1723                            }
1724                            PhaseItemExtraIndex::DynamicOffset(_) | PhaseItemExtraIndex::None => {
1725                                false
1726                            }
1727                        },
1728                    )) =>
1729            {
1730                // This is the normal case on non-WebGL 2.
1731                instance_range.end += 1;
1732            }
1733
1734            UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. } => {
1735                // We thought we were in non-WebGL 2 mode, but we got a dynamic
1736                // offset or non-contiguous index anyway. This shouldn't happen,
1737                // but let's go ahead and do the sensible thing anyhow: demote
1738                // the compressed `NoDynamicOffsets` field to the full
1739                // `DynamicOffsets` array.
1740                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/render_phase/mod.rs:1740",
                        "bevy_render::render_phase", ::tracing::Level::WARN,
                        ::tracing_core::__macro_support::Option::Some("src/render_phase/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(1740u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::render_phase"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::WARN <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::WARN <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Unbatchable binned entity index set was demoted from sparse to dense. This is a bug in the renderer. Please report it.")
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};warn!(
1741                    "Unbatchable binned entity index set was demoted from sparse to dense. \
1742                    This is a bug in the renderer. Please report it.",
1743                );
1744                let new_dynamic_offsets = (0..instance_range.len() as u32)
1745                    .flat_map(|entity_index| self.indices_for_entity_index(entity_index))
1746                    .chain(iter::once(indices))
1747                    .collect();
1748                *self = UnbatchableBinnedEntityIndexSet::Dense(new_dynamic_offsets);
1749            }
1750
1751            UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => {
1752                dense_indices.push(indices);
1753            }
1754        }
1755    }
1756
1757    /// Clears the unbatchable binned entity index set.
1758    fn clear(&mut self) {
1759        match self {
1760            UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => dense_indices.clear(),
1761            UnbatchableBinnedEntityIndexSet::Sparse { .. } => {
1762                *self = UnbatchableBinnedEntityIndexSet::NoEntities;
1763            }
1764            _ => {}
1765        }
1766    }
1767}
1768
1769/// A collection of all items to be rendered that will be encoded to GPU
1770/// commands for a single render phase for a single view.
1771///
1772/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
1773/// They are used to queue entities for rendering.
1774/// Multiple phases might be required due to different sorting/batching behaviors
1775/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
1776/// the rendered texture of the previous phase (e.g. for screen-space reflections).
1777/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
1778/// The render pass might be reused for multiple phases to reduce GPU overhead.
1779///
1780/// This flavor of render phase is used only for meshes that need to be sorted
1781/// back-to-front, such as transparent meshes. For items that don't need strict
1782/// sorting, [`BinnedRenderPhase`] is preferred, for performance.
1783pub struct SortedRenderPhase<I>
1784where
1785    I: SortedPhaseItem,
1786{
1787    /// The items within this [`SortedRenderPhase`].
1788    pub items: IndexMap<(Entity, MainEntity), I, EntityHash>,
1789    /// Items within this render phase that will be automatically removed after
1790    /// this frame.
1791    pub transient_items: Vec<(Entity, MainEntity)>,
1792}
1793
1794impl<I> Default for SortedRenderPhase<I>
1795where
1796    I: SortedPhaseItem,
1797{
1798    fn default() -> Self {
1799        Self {
1800            items: IndexMap::default(),
1801            transient_items: ::alloc::vec::Vec::new()vec![],
1802        }
1803    }
1804}
1805
1806impl<I> SortedRenderPhase<I>
1807where
1808    I: SortedPhaseItem,
1809{
1810    /// Adds a [`PhaseItem`] to this render phase, which will persist between
1811    /// frames until removed.
1812    #[inline]
1813    pub fn add_retained(&mut self, item: I) {
1814        self.items.insert((item.entity(), item.main_entity()), item);
1815    }
1816
1817    /// Adds a [`PhaseItem`] to this render phase, which will be automatically
1818    /// removed after this frame.
1819    #[inline]
1820    pub fn add_transient(&mut self, item: I) {
1821        let key = (item.entity(), item.main_entity());
1822        self.items.insert(key, item);
1823        self.transient_items.push(key);
1824    }
1825
1826    /// Removes the [`PhaseItem`] corresponding to the given main-world entity
1827    /// from this render phase.
1828    #[inline]
1829    pub fn remove(&mut self, render_entity: Entity, main_entity: MainEntity) {
1830        self.items.swap_remove(&(render_entity, main_entity));
1831    }
1832
1833    /// Removes all [`PhaseItem`]s from this render phase.
1834    #[inline]
1835    pub fn clear(&mut self) {
1836        self.items.clear();
1837    }
1838
1839    /// Populates whatever internal fields are necessary in order to perform the
1840    /// sort.
1841    ///
1842    /// For example, for transparent 3D phases, this calculates the distance
1843    /// from each object to the view.
1844    pub fn recalculate_sort_keys(&mut self, view: &ExtractedView) {
1845        I::recalculate_sort_keys(&mut self.items, view);
1846    }
1847
1848    /// Sorts all of its [`PhaseItem`]s.
1849    pub fn sort(&mut self) {
1850        I::sort(&mut self.items);
1851    }
1852
1853    /// An [`Iterator`] through the associated [`Entity`] for each [`PhaseItem`] in order.
1854    #[inline]
1855    pub fn iter_entities(&'_ self) -> impl Iterator<Item = Entity> + '_ {
1856        self.items.values().map(PhaseItem::entity)
1857    }
1858
1859    /// Renders all of its [`PhaseItem`]s using their corresponding draw functions.
1860    pub fn render<'w>(
1861        &self,
1862        render_pass: &mut TrackedRenderPass<'w>,
1863        world: &'w World,
1864        view: Entity,
1865    ) -> Result<(), DrawError> {
1866        self.render_range(render_pass, world, view, ..)
1867    }
1868
1869    /// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
1870    pub fn render_range<'w>(
1871        &self,
1872        render_pass: &mut TrackedRenderPass<'w>,
1873        world: &'w World,
1874        view: Entity,
1875        range: impl RangeBounds<usize>,
1876    ) -> Result<(), DrawError> {
1877        let items = self
1878            .items
1879            .get_range(range)
1880            .expect("`Range` provided to `render_range()` is out of bounds");
1881
1882        let draw_functions = world.resource::<DrawFunctions<I>>();
1883        let mut draw_functions = draw_functions.write();
1884        draw_functions.prepare(world);
1885
1886        let mut index = 0;
1887        while index < items.len() {
1888            let item = &items[index];
1889            let batch_range = item.batch_range();
1890            if batch_range.is_empty() {
1891                index += 1;
1892            } else {
1893                let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
1894                draw_function.draw(world, render_pass, view, item)?;
1895                index += batch_range.len();
1896            }
1897        }
1898        Ok(())
1899    }
1900}
1901
1902/// An item (entity of the render world) which will be drawn to a texture or the screen,
1903/// as part of a render phase.
1904///
1905/// The data required for rendering an entity is extracted from the main world in the
1906/// [`ExtractSchedule`](crate::ExtractSchedule).
1907/// Then it has to be queued up for rendering during the [`RenderSystems::Queue`],
1908/// by adding a corresponding phase item to a render phase.
1909/// Afterwards it will be possibly sorted and rendered automatically in the
1910/// [`RenderSystems::PhaseSort`] and [`RenderSystems::Render`], respectively.
1911///
1912/// `PhaseItem`s come in two flavors: [`BinnedPhaseItem`]s and
1913/// [`SortedPhaseItem`]s.
1914///
1915/// * Binned phase items have a `BinKey` which specifies what bin they're to be
1916///   placed in. All items in the same bin are eligible to be batched together.
1917///   The `BinKey`s are sorted, but the individual bin items aren't. Binned phase
1918///   items are good for opaque meshes, in which the order of rendering isn't
1919///   important. Generally, binned phase items are faster than sorted phase items.
1920///
1921/// * Sorted phase items, on the other hand, are placed into one large buffer
1922///   and then sorted all at once. This is needed for transparent meshes, which
1923///   have to be sorted back-to-front to render with the painter's algorithm.
1924///   These types of phase items are generally slower than binned phase items.
1925pub trait PhaseItem: Sized + Send + Sync + 'static {
1926    /// Whether or not this `PhaseItem` should be subjected to automatic batching. (Default: `true`)
1927    const AUTOMATIC_BATCHING: bool = true;
1928
1929    /// The corresponding entity that will be drawn.
1930    ///
1931    /// This is used to fetch the render data of the entity, required by the draw function,
1932    /// from the render world .
1933    fn entity(&self) -> Entity;
1934
1935    /// The main world entity represented by this `PhaseItem`.
1936    fn main_entity(&self) -> MainEntity;
1937
1938    /// Specifies the [`Draw`] function used to render the item.
1939    fn draw_function(&self) -> DrawFunctionId;
1940
1941    /// The range of instances that the batch covers. After doing a batched draw, batch range
1942    /// length phase items will be skipped. This design is to avoid having to restructure the
1943    /// render phase unnecessarily.
1944    fn batch_range(&self) -> &Range<u32>;
1945    fn batch_range_mut(&mut self) -> &mut Range<u32>;
1946
1947    /// Returns the [`PhaseItemExtraIndex`].
1948    ///
1949    /// If present, this is either a dynamic offset or an indirect parameters
1950    /// index.
1951    fn extra_index(&self) -> PhaseItemExtraIndex;
1952
1953    /// Returns a pair of mutable references to both the batch range and extra
1954    /// index.
1955    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex);
1956}
1957
1958/// The "extra index" associated with some [`PhaseItem`]s, alongside the
1959/// indirect instance index.
1960///
1961/// Sometimes phase items require another index in addition to the range of
1962/// instances they already have. These can be:
1963///
1964/// * The *dynamic offset*: a `wgpu` dynamic offset into the uniform buffer of
1965///   instance data. This is used on platforms that don't support storage
1966///   buffers, to work around uniform buffer size limitations.
1967///
1968/// * The *indirect parameters index*: an index into the buffer that specifies
1969///   the indirect parameters for this [`PhaseItem`]'s drawcall. This is used when
1970///   indirect mode is on (as used for GPU culling).
1971///
1972/// Note that our indirect draw functionality requires storage buffers, so it's
1973/// impossible to have both a dynamic offset and an indirect parameters index.
1974/// This convenient fact allows us to pack both indices into a single `u32`.
1975#[derive(#[automatically_derived]
impl ::core::clone::Clone for PhaseItemExtraIndex {
    #[inline]
    fn clone(&self) -> PhaseItemExtraIndex {
        match self {
            PhaseItemExtraIndex::None => PhaseItemExtraIndex::None,
            PhaseItemExtraIndex::DynamicOffset(__self_0) =>
                PhaseItemExtraIndex::DynamicOffset(::core::clone::Clone::clone(__self_0)),
            PhaseItemExtraIndex::IndirectParametersIndex {
                range: __self_0, batch_set_index: __self_1 } =>
                PhaseItemExtraIndex::IndirectParametersIndex {
                    range: ::core::clone::Clone::clone(__self_0),
                    batch_set_index: ::core::clone::Clone::clone(__self_1),
                },
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for PhaseItemExtraIndex {
    #[inline]
    fn eq(&self, other: &PhaseItemExtraIndex) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (PhaseItemExtraIndex::DynamicOffset(__self_0),
                    PhaseItemExtraIndex::DynamicOffset(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (PhaseItemExtraIndex::IndirectParametersIndex {
                    range: __self_0, batch_set_index: __self_1 },
                    PhaseItemExtraIndex::IndirectParametersIndex {
                    range: __arg1_0, batch_set_index: __arg1_1 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for PhaseItemExtraIndex {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u32>;
        let _: ::core::cmp::AssertParamIsEq<Range<u32>>;
        let _: ::core::cmp::AssertParamIsEq<Option<NonMaxU32>>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for PhaseItemExtraIndex {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state);
        match self {
            PhaseItemExtraIndex::DynamicOffset(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            PhaseItemExtraIndex::IndirectParametersIndex {
                range: __self_0, batch_set_index: __self_1 } => {
                ::core::hash::Hash::hash(__self_0, state);
                ::core::hash::Hash::hash(__self_1, state)
            }
            _ => {}
        }
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for PhaseItemExtraIndex {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            PhaseItemExtraIndex::None =>
                ::core::fmt::Formatter::write_str(f, "None"),
            PhaseItemExtraIndex::DynamicOffset(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "DynamicOffset", &__self_0),
            PhaseItemExtraIndex::IndirectParametersIndex {
                range: __self_0, batch_set_index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "IndirectParametersIndex", "range", __self_0,
                    "batch_set_index", &__self_1),
        }
    }
}Debug)]
1976pub enum PhaseItemExtraIndex {
1977    /// No extra index is present.
1978    None,
1979    /// A `wgpu` dynamic offset into the uniform buffer of instance data. This
1980    /// is used on platforms that don't support storage buffers, to work around
1981    /// uniform buffer size limitations.
1982    DynamicOffset(u32),
1983    /// An index into the buffer that specifies the indirect parameters for this
1984    /// [`PhaseItem`]'s drawcall. This is used when indirect mode is on (as used
1985    /// for GPU culling).
1986    IndirectParametersIndex {
1987        /// The range of indirect parameters within the indirect parameters array.
1988        ///
1989        /// If we're using `multi_draw_indirect_count`, this specifies the
1990        /// maximum range of indirect parameters within that array. If batches
1991        /// are ultimately culled out on the GPU, the actual number of draw
1992        /// commands might be lower than the length of this range.
1993        range: Range<u32>,
1994        /// If `multi_draw_indirect_count` is in use, and this phase item is
1995        /// part of a batch set, specifies the index of the batch set that this
1996        /// phase item is a part of.
1997        ///
1998        /// If `multi_draw_indirect_count` isn't in use, or this phase item
1999        /// isn't part of a batch set, this is `None`.
2000        batch_set_index: Option<NonMaxU32>,
2001    },
2002}
2003
2004impl PhaseItemExtraIndex {
2005    /// Returns either an indirect parameters index or
2006    /// [`PhaseItemExtraIndex::None`], as appropriate.
2007    pub fn maybe_indirect_parameters_index(
2008        indirect_parameters_index: Option<NonMaxU32>,
2009    ) -> PhaseItemExtraIndex {
2010        match indirect_parameters_index {
2011            Some(indirect_parameters_index) => PhaseItemExtraIndex::IndirectParametersIndex {
2012                range: u32::from(indirect_parameters_index)
2013                    ..(u32::from(indirect_parameters_index) + 1),
2014                batch_set_index: None,
2015            },
2016            None => PhaseItemExtraIndex::None,
2017        }
2018    }
2019
2020    /// Returns either a dynamic offset index or [`PhaseItemExtraIndex::None`],
2021    /// as appropriate.
2022    pub fn maybe_dynamic_offset(dynamic_offset: Option<NonMaxU32>) -> PhaseItemExtraIndex {
2023        match dynamic_offset {
2024            Some(dynamic_offset) => PhaseItemExtraIndex::DynamicOffset(dynamic_offset.into()),
2025            None => PhaseItemExtraIndex::None,
2026        }
2027    }
2028}
2029
2030/// Represents phase items that are placed into bins. The `BinKey` specifies
2031/// which bin they're to be placed in. Bin keys are sorted, and items within the
2032/// same bin are eligible to be batched together. The elements within the bins
2033/// aren't themselves sorted.
2034///
2035/// An example of a binned phase item is `Opaque3d`, for which the rendering
2036/// order isn't critical.
2037pub trait BinnedPhaseItem: PhaseItem {
2038    /// The key used for binning [`PhaseItem`]s into bins. Order the members of
2039    /// [`BinnedPhaseItem::BinKey`] by the order of binding for best
2040    /// performance. For example, pipeline id, draw function id, mesh asset id,
2041    /// lowest variable bind group id such as the material bind group id, and
2042    /// its dynamic offsets if any, next bind group and offsets, etc. This
2043    /// reduces the need for rebinding between bins and improves performance.
2044    type BinKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash;
2045
2046    /// The key used to combine batches into batch sets.
2047    ///
2048    /// A *batch set* is a set of meshes that can potentially be multi-drawn
2049    /// together.
2050    type BatchSetKey: PhaseItemBatchSetKey;
2051
2052    /// Creates a new binned phase item from the key and per-entity data.
2053    ///
2054    /// Unlike [`SortedPhaseItem`]s, this is generally called "just in time"
2055    /// before rendering. The resulting phase item isn't stored in any data
2056    /// structures, resulting in significant memory savings.
2057    fn new(
2058        batch_set_key: Self::BatchSetKey,
2059        bin_key: Self::BinKey,
2060        representative_entity: (Entity, MainEntity),
2061        batch_range: Range<u32>,
2062        extra_index: PhaseItemExtraIndex,
2063    ) -> Self;
2064}
2065
2066/// A key used to combine batches into batch sets.
2067///
2068/// A *batch set* is a set of meshes that can potentially be multi-drawn
2069/// together.
2070pub trait PhaseItemBatchSetKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash {
2071    /// Returns true if this batch set key describes indexed meshes or false if
2072    /// it describes non-indexed meshes.
2073    ///
2074    /// Bevy uses this in order to determine which kind of indirect draw
2075    /// parameters to use, if indirect drawing is enabled.
2076    fn indexed(&self) -> bool;
2077}
2078
2079/// Represents phase items that must be sorted. The `SortKey` specifies the
2080/// order that these items are drawn in. These are placed into a single array,
2081/// and the array as a whole is then sorted.
2082///
2083/// An example of a sorted phase item is `Transparent3d`, which must be sorted
2084/// back to front in order to correctly render with the painter's algorithm.
2085pub trait SortedPhaseItem: PhaseItem {
2086    /// The type used for ordering the items. The smallest values are drawn first.
2087    /// This order can be calculated using the [`ViewRangefinder3d`],
2088    /// based on the view-space `Z` value of the corresponding view matrix.
2089    type SortKey: Ord;
2090
2091    /// Determines the order in which the items are drawn.
2092    fn sort_key(&self) -> Self::SortKey;
2093
2094    /// Sorts a slice of phase items into render order. Generally if the same type
2095    /// is batched this should use a stable sort like [`slice::sort_by_key`].
2096    /// In almost all other cases, this should not be altered from the default,
2097    /// which uses an unstable sort, as this provides the best balance of CPU and GPU
2098    /// performance.
2099    ///
2100    /// Implementers can optionally not sort the list at all. This is generally advisable if and
2101    /// only if the renderer supports a depth prepass, which is by default not supported by
2102    /// the rest of Bevy's first party rendering crates. Even then, this may have a negative
2103    /// impact on GPU-side performance due to overdraw.
2104    ///
2105    /// It's advised to always profile for performance changes when changing this implementation.
2106    #[inline]
2107    fn sort(items: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>) {
2108        items.sort_unstable_by_key(|_, value| Self::sort_key(value));
2109    }
2110
2111    /// Populates whatever internal fields are necessary in order to perform the
2112    /// sort.
2113    ///
2114    /// The renderer calls this method right before calling [`Self::sort`]. For
2115    /// 3D transparent phases that need to be depth sorted, it populates the
2116    /// `distance` field with the actual distance from the view. For other
2117    /// phases, this method is generally a no-op.
2118    fn recalculate_sort_keys(
2119        items: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
2120        view: &ExtractedView,
2121    );
2122
2123    /// Whether this phase item targets indexed meshes (those with both vertex
2124    /// and index buffers as opposed to just vertex buffers).
2125    ///
2126    /// Bevy needs this information in order to properly group phase items
2127    /// together for multi-draw indirect, because the GPU layout of indirect
2128    /// commands differs between indexed and non-indexed meshes.
2129    ///
2130    /// If you're implementing a custom phase item that doesn't describe a mesh,
2131    /// you can safely return false here.
2132    fn indexed(&self) -> bool;
2133}
2134
2135/// A [`PhaseItem`] item, that automatically sets the appropriate render pipeline,
2136/// cached in the [`PipelineCache`].
2137///
2138/// You can use the [`SetItemPipeline`] render command to set the pipeline for this item.
2139pub trait CachedRenderPipelinePhaseItem: PhaseItem {
2140    /// The id of the render pipeline, cached in the [`PipelineCache`], that will be used to draw
2141    /// this phase item.
2142    fn cached_pipeline(&self) -> CachedRenderPipelineId;
2143}
2144
2145/// A [`RenderCommand`] that sets the pipeline for the [`CachedRenderPipelinePhaseItem`].
2146pub struct SetItemPipeline;
2147
2148impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
2149    type Param = SRes<PipelineCache>;
2150    type ViewQuery = ();
2151    type ItemQuery = ();
2152    #[inline]
2153    fn render<'w>(
2154        item: &P,
2155        _view: (),
2156        _entity: Option<()>,
2157        pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
2158        pass: &mut TrackedRenderPass<'w>,
2159    ) -> RenderCommandResult {
2160        if let Some(pipeline) = pipeline_cache
2161            .into_inner()
2162            .get_render_pipeline(item.cached_pipeline())
2163        {
2164            pass.set_render_pipeline(pipeline);
2165            RenderCommandResult::Success
2166        } else {
2167            RenderCommandResult::Skip
2168        }
2169    }
2170}
2171
2172/// This system sorts the [`PhaseItem`]s of all [`SortedRenderPhase`]s of this
2173/// type.
2174pub fn sort_phase_system<I>(
2175    views: Query<&ExtractedView>,
2176    mut render_phases: ResMut<ViewSortedRenderPhases<I>>,
2177) where
2178    I: SortedPhaseItem,
2179{
2180    for view in &views {
2181        let Some(phase) = render_phases.get_mut(&view.retained_view_entity) else {
2182            continue;
2183        };
2184        phase.recalculate_sort_keys(view);
2185        phase.sort();
2186    }
2187}
2188
2189impl BinnedRenderPhaseType {
2190    pub fn mesh(
2191        batchable: bool,
2192        gpu_preprocessing_support: &GpuPreprocessingSupport,
2193    ) -> BinnedRenderPhaseType {
2194        match (batchable, gpu_preprocessing_support.max_supported_mode) {
2195            (true, GpuPreprocessingMode::Culling) => BinnedRenderPhaseType::MultidrawableMesh,
2196            (true, _) => BinnedRenderPhaseType::BatchableMesh,
2197            (false, _) => BinnedRenderPhaseType::UnbatchableMesh,
2198        }
2199    }
2200}
2201
2202impl RenderBin {
2203    /// Creates a [`RenderBin`] containing a single entity.
2204    fn from_entity(entity: MainEntity, uniform_index: InputUniformIndex) -> RenderBin {
2205        let mut entities = IndexMap::default();
2206        entities.insert(entity, uniform_index);
2207        RenderBin { entities }
2208    }
2209
2210    /// Inserts an entity into the bin.
2211    fn insert(&mut self, entity: MainEntity, uniform_index: InputUniformIndex) {
2212        self.entities.insert(entity, uniform_index);
2213    }
2214
2215    /// Removes an entity from the bin.
2216    fn remove(&mut self, entity_to_remove: MainEntity) {
2217        self.entities.swap_remove(&entity_to_remove);
2218    }
2219
2220    /// Returns true if the bin contains no entities.
2221    fn is_empty(&self) -> bool {
2222        self.entities.is_empty()
2223    }
2224
2225    /// Returns the [`IndexMap`] containing all the entities in the bin, along
2226    /// with the cached [`InputUniformIndex`] of each.
2227    #[inline]
2228    pub fn entities(&self) -> &IndexMap<MainEntity, InputUniformIndex, EntityHash> {
2229        &self.entities
2230    }
2231}
2232
2233#[cfg(test)]
2234mod tests {
2235    use proptest_derive::Arbitrary;
2236
2237    use crate::render_phase::GpuRenderBinnedMeshInstance;
2238
2239    /// A `proptest`-based randomized test for `RenderMultidrawableBatchSet`.
2240    ///
2241    /// `proptest` works by generating random test cases and performing checks.
2242    /// We use it to generate random sets of entity bin insertion and removal
2243    /// operations, then verify that the data structure is consistent and that
2244    /// all invariants are upheld.
2245    #[test]
2246    #[expect(
2247        non_local_definitions,
2248        reason = "`derive(Arbitrary)` generates an impl here"
2249    )]
2250    fn render_multidrawable_batch_set() {
2251        use super::RenderMultidrawableBatchSet;
2252
2253        use core::ops::Range;
2254
2255        use bevy_ecs::entity::{Entity, EntityIndex};
2256        use bevy_material::labels::DrawFunctionId;
2257        use proptest::{bool, collection, test_runner::TestRunner};
2258
2259        use crate::{
2260            render_phase::{
2261                BinnedPhaseItem, InputUniformIndex, PhaseItem, PhaseItemBatchSetKey, RenderBinIndex,
2262            },
2263            sync_world::{MainEntity, MainEntityHashMap, MainEntityHashSet},
2264        };
2265
2266        /// A fake `BinnedPhaseItem` that we use for testing.
2267        #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
2268        struct MockBinnedPhaseItem;
2269
2270        /// A fake `BinnedPhaseItem::BinKey` that we use for testing.
2271        ///
2272        /// The bin key should match the bin index.
2273        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
2274        struct MockBinnedPhaseItemBinKey(u32);
2275
2276        /// A fake `BinnedPhaseItem::BatchKey` that we use for testing.
2277        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2278        struct MockBinnedPhaseItemBatchSetKey;
2279
2280        impl BinnedPhaseItem for MockBinnedPhaseItem {
2281            type BinKey = MockBinnedPhaseItemBinKey;
2282
2283            type BatchSetKey = MockBinnedPhaseItemBatchSetKey;
2284
2285            fn new(
2286                _: Self::BatchSetKey,
2287                _: Self::BinKey,
2288                _: (Entity, MainEntity),
2289                _: Range<u32>,
2290                _: super::PhaseItemExtraIndex,
2291            ) -> Self {
2292                Self
2293            }
2294        }
2295
2296        impl PhaseItem for MockBinnedPhaseItem {
2297            fn entity(&self) -> Entity {
2298                unimplemented!()
2299            }
2300
2301            fn main_entity(&self) -> MainEntity {
2302                unimplemented!()
2303            }
2304
2305            fn draw_function(&self) -> DrawFunctionId {
2306                unimplemented!()
2307            }
2308
2309            fn batch_range(&self) -> &Range<u32> {
2310                unimplemented!()
2311            }
2312
2313            fn batch_range_mut(&mut self) -> &mut Range<u32> {
2314                unimplemented!()
2315            }
2316
2317            fn extra_index(&self) -> super::PhaseItemExtraIndex {
2318                unimplemented!()
2319            }
2320
2321            fn batch_range_and_extra_index_mut(
2322                &mut self,
2323            ) -> (&mut Range<u32>, &mut super::PhaseItemExtraIndex) {
2324                unimplemented!()
2325            }
2326        }
2327
2328        impl PhaseItemBatchSetKey for MockBinnedPhaseItemBatchSetKey {
2329            fn indexed(&self) -> bool {
2330                // Doesn't matter. We arbitrarily return true.
2331                true
2332            }
2333        }
2334
2335        /// A single operation that we perform on the multidrawable batch set.
2336        #[derive(Arbitrary, Debug)]
2337        enum RenderMultidrawableBatchSetOperation {
2338            /// Add an entity (mock mesh instance) to the batch set.
2339            Add {
2340                /// The ID of the entity.
2341                #[proptest(strategy = "0..32u32")]
2342                entity_id: u32,
2343                /// The index of the bin that we place the entity into.
2344                #[proptest(strategy = "0..8u32")]
2345                bin_index: u32,
2346                /// The input uniform index associated with the entity.
2347                #[proptest(strategy = "0..1024u32")]
2348                input_uniform_index: u32,
2349            },
2350            /// Remove an entity (mock mesh instance) from the batch set.
2351            Remove {
2352                /// The ID of the entity.
2353                #[proptest(strategy = "0..32u32")]
2354                entity_id: u32,
2355            },
2356        }
2357
2358        /// A "control" structure that stores the expected contents of the
2359        /// multidrawable batch set.
2360        ///
2361        /// This is essentially a simpler, but inefficient, version of
2362        /// `RenderMultidrawableBatchSet` that we use to check that the
2363        /// invariants of `RenderMultidrawableBatchSet` are being upheld.
2364        struct ExpectedMultidrawableBatchSet {
2365            /// A mapping from each bin index to the entities within it.
2366            bin_index_to_entities: Vec<MainEntityHashSet>,
2367            /// A mapping from each entity ID to the binned mesh instance data.
2368            entity_to_binned_mesh_instance: MainEntityHashMap<GpuRenderBinnedMeshInstance>,
2369        }
2370
2371        impl ExpectedMultidrawableBatchSet {
2372            /// Inserts an entity into the given bin of the control structure.
2373            fn insert(
2374                &mut self,
2375                entity: MainEntity,
2376                bin_index: RenderBinIndex,
2377                input_uniform_index: InputUniformIndex,
2378            ) {
2379                self.entity_to_binned_mesh_instance.insert(
2380                    entity,
2381                    GpuRenderBinnedMeshInstance {
2382                        bin_index: bin_index.0,
2383                        input_uniform_index: input_uniform_index.0,
2384                    },
2385                );
2386                self.bin_index_to_entities[bin_index.0 as usize].insert(entity);
2387            }
2388
2389            /// Removes an entity from the control structure and returns its instance.
2390            fn remove(&mut self, entity: MainEntity) -> GpuRenderBinnedMeshInstance {
2391                let render_binned_mesh_instance =
2392                    self.entity_to_binned_mesh_instance.remove(&entity).unwrap();
2393                self.bin_index_to_entities[render_binned_mesh_instance.bin_index as usize]
2394                    .remove(&entity);
2395                render_binned_mesh_instance
2396            }
2397        }
2398
2399        let mut runner = TestRunner::default();
2400        runner
2401            .run(
2402                // Generate up to 1024 random operations.
2403                //
2404                // Invalid operations (attempting to bin an entity that's
2405                // already binned or attempting to unbin an entity that wasn't
2406                // binned) will be skipped.
2407                &collection::vec(
2408                    proptest::prelude::any::<RenderMultidrawableBatchSetOperation>(),
2409                    0..1024,
2410                ),
2411                |ops| {
2412                    // Create the data structure to test.
2413                    let mut batch_set = RenderMultidrawableBatchSet::<MockBinnedPhaseItem>::new();
2414
2415                    // Create the control data structure.
2416                    let mut expected = ExpectedMultidrawableBatchSet {
2417                        bin_index_to_entities: vec![MainEntityHashSet::default(); 1024],
2418                        entity_to_binned_mesh_instance: MainEntityHashMap::default(),
2419                    };
2420
2421                    // Process each operation, skipping invalid ones.
2422                    for op in ops.iter() {
2423                        match *op {
2424                            RenderMultidrawableBatchSetOperation::Add {
2425                                entity_id,
2426                                bin_index,
2427                                input_uniform_index,
2428                            } => {
2429                                let entity = MainEntity::from(Entity::from_index(
2430                                    EntityIndex::from_raw_u32(entity_id).unwrap(),
2431                                ));
2432                                let input_uniform_index = InputUniformIndex(input_uniform_index);
2433
2434                                // Skip this operation if it's trying to add an entity that's already binned.
2435                                if expected
2436                                    .entity_to_binned_mesh_instance
2437                                    .contains_key(&entity)
2438                                {
2439                                    continue;
2440                                }
2441
2442                                // Insert into the expected and actual data
2443                                // structures.
2444                                expected.insert(
2445                                    entity,
2446                                    RenderBinIndex(bin_index),
2447                                    input_uniform_index,
2448                                );
2449                                batch_set.insert(
2450                                    MockBinnedPhaseItemBinKey(bin_index),
2451                                    entity,
2452                                    input_uniform_index,
2453                                );
2454                            }
2455
2456                            RenderMultidrawableBatchSetOperation::Remove { entity_id } => {
2457                                let entity = MainEntity::from(Entity::from_index(
2458                                    EntityIndex::from_raw_u32(entity_id).unwrap(),
2459                                ));
2460
2461                                // Skip this operation if it's trying to remove
2462                                // an entity that wasn't already binned.
2463                                if !expected
2464                                    .entity_to_binned_mesh_instance
2465                                    .contains_key(&entity)
2466                                {
2467                                    continue;
2468                                }
2469
2470                                // Insert into the expected and actual data
2471                                // structures.
2472                                let render_binned_mesh_instance = expected.remove(entity);
2473                                batch_set.remove(
2474                                    entity,
2475                                    &MockBinnedPhaseItemBinKey(
2476                                        render_binned_mesh_instance.bin_index,
2477                                    ),
2478                                );
2479                            }
2480                        }
2481                    }
2482
2483                    // Verify that the batch set invariants are upheld.
2484                    verify(&batch_set, &expected);
2485
2486                    Ok(())
2487                },
2488            )
2489            .unwrap();
2490
2491        // Verifies that the given `batch_set` matches the expected batch set
2492        // and ensures that the invariants of that batch set are upheld.
2493        fn verify(
2494            batch_set: &RenderMultidrawableBatchSet<MockBinnedPhaseItem>,
2495            expected: &ExpectedMultidrawableBatchSet,
2496        ) {
2497            // Verify every entity is present.
2498            verify_entity_presence(
2499                batch_set,
2500                &expected.bin_index_to_entities,
2501                &expected.entity_to_binned_mesh_instance,
2502            );
2503
2504            // Verify that the binned mesh instance GPU buffer is correct.
2505            verify_render_binned_mesh_instance_buffer(batch_set, &expected.bin_index_to_entities);
2506
2507            // Verify that no indirect parameter offsets overlap.
2508            verify_indirect_parameters_offsets(batch_set);
2509        }
2510
2511        /// Verifies that every entity is present in the multidrawable batch
2512        /// set after modifications.
2513        fn verify_entity_presence(
2514            batch_set: &RenderMultidrawableBatchSet<MockBinnedPhaseItem>,
2515            expected: &[MainEntityHashSet],
2516            entity_to_bin_index_and_input_uniform_index: &MainEntityHashMap<
2517                GpuRenderBinnedMeshInstance,
2518            >,
2519        ) {
2520            for (bin_key_index, expected_entities) in expected.iter().enumerate() {
2521                let bin_key = MockBinnedPhaseItemBinKey(bin_key_index as u32);
2522                if expected_entities.is_empty() {
2523                    assert!(!batch_set.bin_key_to_bin_index.contains_key(&bin_key));
2524                    continue;
2525                }
2526
2527                let Some(render_bin_index) = batch_set.bin_key_to_bin_index.get(&bin_key) else {
2528                    panic!("Bin not present: key {:?}", bin_key);
2529                };
2530                let Some(render_bin) = batch_set.bin(*render_bin_index) else {
2531                    panic!("Bin not present: index {:?}", render_bin_index);
2532                };
2533                for expected_entity in expected_entities {
2534                    let Some(GpuRenderBinnedMeshInstance {
2535                        bin_index,
2536                        input_uniform_index,
2537                    }) = entity_to_bin_index_and_input_uniform_index.get(expected_entity)
2538                    else {
2539                        panic!(
2540                            "Test harness bug: entity-to-bin-index-and-input-uniform-index \
2541                                table and expected table don't agree"
2542                        );
2543                    };
2544                    assert_eq!(MockBinnedPhaseItemBinKey(*bin_index), bin_key);
2545
2546                    let Some(render_bin_buffer_index) = render_bin
2547                        .entity_to_binned_mesh_instance_index
2548                        .get(expected_entity)
2549                    else {
2550                        panic!("Buffer index not present");
2551                    };
2552                    let render_bin_entry = batch_set
2553                        .gpu_buffers
2554                        .render_binned_mesh_instance_buffer
2555                        .values()[render_bin_buffer_index.0 as usize];
2556                    assert_eq!(render_bin_entry.bin_index, **render_bin_index);
2557                    assert_eq!(render_bin_entry.input_uniform_index, *input_uniform_index);
2558                }
2559            }
2560        }
2561
2562        /// Verifies that the
2563        /// `RenderMultidrawableBatchSet::render_binned_mesh_instances_cpu`
2564        /// contains the correct entity and bin index.
2565        fn verify_render_binned_mesh_instance_buffer(
2566            batch_set: &RenderMultidrawableBatchSet<MockBinnedPhaseItem>,
2567            expected: &[MainEntityHashSet],
2568        ) {
2569            for (render_bin_buffer_index, gpu_render_binned_mesh_instance) in batch_set
2570                .gpu_buffers
2571                .render_binned_mesh_instance_buffer
2572                .values()
2573                .iter()
2574                .enumerate()
2575            {
2576                let binned_mesh_instance_cpu =
2577                    &batch_set.render_binned_mesh_instances_cpu[render_bin_buffer_index];
2578
2579                // Make sure that the `GpuRenderBinnedMeshInstance::bin_index`
2580                // matches the `CpuRenderBinnedMeshInstance::bin_index`.
2581                let gpu_render_bin_index = gpu_render_binned_mesh_instance.bin_index;
2582                assert_eq!(gpu_render_bin_index, *binned_mesh_instance_cpu.bin_index);
2583
2584                let render_bin = batch_set.bins[gpu_render_bin_index as usize]
2585                    .as_ref()
2586                    .unwrap();
2587
2588                // Make sure that the entity in the
2589                // `RenderMultidrawableBin::entity_to_binned_mesh_instance_index`
2590                // table matches the entity in the
2591                // `CpuRenderBinnedMeshInstance`.
2592                let Some(entity) = render_bin
2593                    .entity_to_binned_mesh_instance_index
2594                    .iter()
2595                    .find_map(|(entity, buffer_index)| {
2596                        if render_bin_buffer_index as u32 == buffer_index.0 {
2597                            Some(entity)
2598                        } else {
2599                            None
2600                        }
2601                    })
2602                else {
2603                    panic!(
2604                        "Entity at buffer index {:?} not found in bin {:?}",
2605                        render_bin_buffer_index, gpu_render_bin_index
2606                    );
2607                };
2608                assert_eq!(binned_mesh_instance_cpu.main_entity, *entity);
2609
2610                // Make sure that the bin with the appropriate bin key should
2611                // actually contain the entity.
2612                let Some(bin_key) =
2613                    batch_set
2614                        .bin_key_to_bin_index
2615                        .iter()
2616                        .find_map(|(bin_key, bin_index)| {
2617                            if bin_index.0 == gpu_render_bin_index {
2618                                Some(*bin_key)
2619                            } else {
2620                                None
2621                            }
2622                        })
2623                else {
2624                    panic!(
2625                        "Couldn't find a bin key for bin index {:?}",
2626                        gpu_render_bin_index
2627                    );
2628                };
2629                assert!(expected[bin_key.0 as usize].contains(entity));
2630            }
2631        }
2632
2633        fn verify_indirect_parameters_offsets(
2634            batch_set: &RenderMultidrawableBatchSet<MockBinnedPhaseItem>,
2635        ) {
2636            for (render_bin_index, indirect_parameters_offset) in batch_set
2637                .gpu_buffers
2638                .bin_index_to_indirect_parameters_offset_buffer
2639                .values()
2640                .iter()
2641                .enumerate()
2642            {
2643                if *indirect_parameters_offset == u32::MAX {
2644                    continue;
2645                }
2646                assert_eq!(
2647                    batch_set.indirect_parameters_offset_to_bin_index
2648                        [*indirect_parameters_offset as usize],
2649                    RenderBinIndex(render_bin_index as u32)
2650                );
2651            }
2652            for (indirect_parameters_offset, render_bin_index) in batch_set
2653                .indirect_parameters_offset_to_bin_index
2654                .iter()
2655                .enumerate()
2656            {
2657                assert!(batch_set.bins[render_bin_index.0 as usize].is_some());
2658                assert_eq!(
2659                    *batch_set
2660                        .gpu_buffers
2661                        .bin_index_to_indirect_parameters_offset_buffer
2662                        .get(render_bin_index.0)
2663                        .unwrap(),
2664                    indirect_parameters_offset as u32
2665                );
2666            }
2667        }
2668    }
2669}