bevy_render/render_phase/
mod.rs

1//! The modular rendering abstraction responsible for queuing, preparing, sorting and drawing
2//! entities as part of separate render phases.
3//!
4//! In Bevy each view (camera, or shadow-casting light, etc.) has one or multiple render phases
5//! (e.g. opaque, transparent, shadow, etc).
6//! They are used to queue entities for rendering.
7//! Multiple phases might be required due to different sorting/batching behaviors
8//! (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
9//! the rendered texture of the previous phase (e.g. for screen-space reflections).
10//!
11//! To draw an entity, a corresponding [`PhaseItem`] has to be added to one or multiple of these
12//! render phases for each view that it is visible in.
13//! This must be done in the [`RenderSet::Queue`].
14//! After that the render phase sorts them in the [`RenderSet::PhaseSort`].
15//! Finally the items are rendered using a single [`TrackedRenderPass`], during
16//! the [`RenderSet::Render`].
17//!
18//! Therefore each phase item is assigned a [`Draw`] function.
19//! These set up the state of the [`TrackedRenderPass`] (i.e. select the
20//! [`RenderPipeline`](crate::render_resource::RenderPipeline), configure the
21//! [`BindGroup`](crate::render_resource::BindGroup)s, etc.) and then issue a draw call,
22//! for the corresponding item.
23//!
24//! The [`Draw`] function trait can either be implemented directly or such a function can be
25//! created by composing multiple [`RenderCommand`]s.
26
27mod draw;
28mod draw_state;
29mod rangefinder;
30
31use bevy_app::{App, Plugin};
32use bevy_derive::{Deref, DerefMut};
33use bevy_utils::{default, hashbrown::hash_map::Entry, HashMap};
34pub use draw::*;
35pub use draw_state::*;
36use encase::{internal::WriteInto, ShaderSize};
37use nonmax::NonMaxU32;
38pub use rangefinder::*;
39
40use crate::sync_world::MainEntity;
41use crate::{
42    batching::{
43        self,
44        gpu_preprocessing::{self, BatchedInstanceBuffers},
45        no_gpu_preprocessing::{self, BatchedInstanceBuffer},
46        GetFullBatchData,
47    },
48    render_resource::{CachedRenderPipelineId, GpuArrayBufferIndex, PipelineCache},
49    Render, RenderApp, RenderSet,
50};
51use bevy_ecs::{
52    entity::EntityHashMap,
53    prelude::*,
54    system::{lifetimeless::SRes, SystemParamItem},
55};
56use core::{
57    fmt::{self, Debug, Formatter},
58    hash::Hash,
59    iter,
60    marker::PhantomData,
61    ops::Range,
62    slice::SliceIndex,
63};
64use smallvec::SmallVec;
65
66/// Stores the rendering instructions for a single phase that uses bins in all
67/// views.
68///
69/// They're cleared out every frame, but storing them in a resource like this
70/// allows us to reuse allocations.
71#[derive(Resource, Deref, DerefMut)]
72pub struct ViewBinnedRenderPhases<BPI>(pub EntityHashMap<BinnedRenderPhase<BPI>>)
73where
74    BPI: BinnedPhaseItem;
75
76/// A collection of all rendering instructions, that will be executed by the GPU, for a
77/// single render phase for a single view.
78///
79/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
80/// They are used to queue entities for rendering.
81/// Multiple phases might be required due to different sorting/batching behaviors
82/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
83/// the rendered texture of the previous phase (e.g. for screen-space reflections).
84/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
85/// The render pass might be reused for multiple phases to reduce GPU overhead.
86///
87/// This flavor of render phase is used for phases in which the ordering is less
88/// critical: for example, `Opaque3d`. It's generally faster than the
89/// alternative [`SortedRenderPhase`].
90pub struct BinnedRenderPhase<BPI>
91where
92    BPI: BinnedPhaseItem,
93{
94    /// A list of `BinKey`s for batchable items.
95    ///
96    /// These are accumulated in `queue_material_meshes` and then sorted in
97    /// `batch_and_prepare_binned_render_phase`.
98    pub batchable_mesh_keys: Vec<BPI::BinKey>,
99
100    /// The batchable bins themselves.
101    ///
102    /// Each bin corresponds to a single batch set. For unbatchable entities,
103    /// prefer `unbatchable_values` instead.
104    pub batchable_mesh_values: HashMap<BPI::BinKey, Vec<(Entity, MainEntity)>>,
105
106    /// A list of `BinKey`s for unbatchable items.
107    ///
108    /// These are accumulated in `queue_material_meshes` and then sorted in
109    /// `batch_and_prepare_binned_render_phase`.
110    pub unbatchable_mesh_keys: Vec<BPI::BinKey>,
111
112    /// The unbatchable bins.
113    ///
114    /// Each entity here is rendered in a separate drawcall.
115    pub unbatchable_mesh_values: HashMap<BPI::BinKey, UnbatchableBinnedEntities>,
116
117    /// Items in the bin that aren't meshes at all.
118    ///
119    /// Bevy itself doesn't place anything in this list, but plugins or your app
120    /// can in order to execute custom drawing commands. Draw functions for each
121    /// entity are simply called in order at rendering time.
122    ///
123    /// See the `custom_phase_item` example for an example of how to use this.
124    pub non_mesh_items: Vec<(BPI::BinKey, (Entity, MainEntity))>,
125
126    /// Information on each batch set.
127    ///
128    /// A *batch set* is a set of entities that will be batched together unless
129    /// we're on a platform that doesn't support storage buffers (e.g. WebGL 2)
130    /// and differing dynamic uniform indices force us to break batches. On
131    /// platforms that support storage buffers, a batch set always consists of
132    /// at most one batch.
133    ///
134    /// The unbatchable entities immediately follow the batches in the storage
135    /// buffers.
136    pub(crate) batch_sets: Vec<SmallVec<[BinnedRenderPhaseBatch; 1]>>,
137}
138
139/// Information about a single batch of entities rendered using binned phase
140/// items.
141#[derive(Debug)]
142pub struct BinnedRenderPhaseBatch {
143    /// An entity that's *representative* of this batch.
144    ///
145    /// Bevy uses this to fetch the mesh. It can be any entity in the batch.
146    pub representative_entity: (Entity, MainEntity),
147    /// The range of instance indices in this batch.
148    pub instance_range: Range<u32>,
149
150    /// The dynamic offset of the batch.
151    ///
152    /// Note that dynamic offsets are only used on platforms that don't support
153    /// storage buffers.
154    pub extra_index: PhaseItemExtraIndex,
155}
156
157/// Information about the unbatchable entities in a bin.
158pub struct UnbatchableBinnedEntities {
159    /// The entities.
160    pub entities: Vec<(Entity, MainEntity)>,
161
162    /// The GPU array buffer indices of each unbatchable binned entity.
163    pub(crate) buffer_indices: UnbatchableBinnedEntityIndexSet,
164}
165
166/// Stores instance indices and dynamic offsets for unbatchable entities in a
167/// binned render phase.
168///
169/// This is conceptually `Vec<UnbatchableBinnedEntityDynamicOffset>`, but it
170/// avoids the overhead of storing dynamic offsets on platforms that support
171/// them. In other words, this allows a fast path that avoids allocation on
172/// platforms that aren't WebGL 2.
173#[derive(Default)]
174
175pub(crate) enum UnbatchableBinnedEntityIndexSet {
176    /// There are no unbatchable entities in this bin (yet).
177    #[default]
178    NoEntities,
179
180    /// The instances for all unbatchable entities in this bin are contiguous,
181    /// and there are no dynamic uniforms.
182    ///
183    /// This is the typical case on platforms other than WebGL 2. We special
184    /// case this to avoid allocation on those platforms.
185    Sparse {
186        /// The range of indices.
187        instance_range: Range<u32>,
188        /// The index of the first indirect instance parameters.
189        ///
190        /// The other indices immediately follow these.
191        first_indirect_parameters_index: Option<NonMaxU32>,
192    },
193
194    /// Dynamic uniforms are present for unbatchable entities in this bin.
195    ///
196    /// We fall back to this on WebGL 2.
197    Dense(Vec<UnbatchableBinnedEntityIndices>),
198}
199
200/// The instance index and dynamic offset (if present) for an unbatchable entity.
201///
202/// This is only useful on platforms that don't support storage buffers.
203#[derive(Clone, Copy)]
204pub(crate) struct UnbatchableBinnedEntityIndices {
205    /// The instance index.
206    pub(crate) instance_index: u32,
207    /// The [`PhaseItemExtraIndex`], if present.
208    pub(crate) extra_index: PhaseItemExtraIndex,
209}
210
211/// Identifies the list within [`BinnedRenderPhase`] that a phase item is to be
212/// placed in.
213#[derive(Clone, Copy, PartialEq, Debug)]
214pub enum BinnedRenderPhaseType {
215    /// The item is a mesh that's eligible for indirect rendering and can be
216    /// batched with other meshes of the same type.
217    BatchableMesh,
218
219    /// The item is a mesh that's eligible for indirect rendering, but can't be
220    /// batched with other meshes of the same type.
221    ///
222    /// At the moment, this is used for skinned meshes.
223    UnbatchableMesh,
224
225    /// The item isn't a mesh at all.
226    ///
227    /// Bevy will simply invoke the drawing commands for such items one after
228    /// another, with no further processing.
229    ///
230    /// The engine itself doesn't enqueue any items of this type, but it's
231    /// available for use in your application and/or plugins.
232    NonMesh,
233}
234
235impl<T> From<GpuArrayBufferIndex<T>> for UnbatchableBinnedEntityIndices
236where
237    T: Clone + ShaderSize + WriteInto,
238{
239    fn from(value: GpuArrayBufferIndex<T>) -> Self {
240        UnbatchableBinnedEntityIndices {
241            instance_index: value.index,
242            extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(value.dynamic_offset),
243        }
244    }
245}
246
247impl<BPI> Default for ViewBinnedRenderPhases<BPI>
248where
249    BPI: BinnedPhaseItem,
250{
251    fn default() -> Self {
252        Self(default())
253    }
254}
255
256impl<BPI> ViewBinnedRenderPhases<BPI>
257where
258    BPI: BinnedPhaseItem,
259{
260    pub fn insert_or_clear(&mut self, entity: Entity) {
261        match self.entry(entity) {
262            Entry::Occupied(mut entry) => entry.get_mut().clear(),
263            Entry::Vacant(entry) => {
264                entry.insert(default());
265            }
266        }
267    }
268}
269
270impl<BPI> BinnedRenderPhase<BPI>
271where
272    BPI: BinnedPhaseItem,
273{
274    /// Bins a new entity.
275    ///
276    /// The `phase_type` parameter specifies whether the entity is a
277    /// preprocessable mesh and whether it can be binned with meshes of the same
278    /// type.
279    pub fn add(
280        &mut self,
281        key: BPI::BinKey,
282        entity: (Entity, MainEntity),
283        phase_type: BinnedRenderPhaseType,
284    ) {
285        match phase_type {
286            BinnedRenderPhaseType::BatchableMesh => {
287                match self.batchable_mesh_values.entry(key.clone()) {
288                    Entry::Occupied(mut entry) => entry.get_mut().push(entity),
289                    Entry::Vacant(entry) => {
290                        self.batchable_mesh_keys.push(key);
291                        entry.insert(vec![entity]);
292                    }
293                }
294            }
295
296            BinnedRenderPhaseType::UnbatchableMesh => {
297                match self.unbatchable_mesh_values.entry(key.clone()) {
298                    Entry::Occupied(mut entry) => entry.get_mut().entities.push(entity),
299                    Entry::Vacant(entry) => {
300                        self.unbatchable_mesh_keys.push(key);
301                        entry.insert(UnbatchableBinnedEntities {
302                            entities: vec![entity],
303                            buffer_indices: default(),
304                        });
305                    }
306                }
307            }
308
309            BinnedRenderPhaseType::NonMesh => {
310                // We don't process these items further.
311                self.non_mesh_items.push((key, entity));
312            }
313        }
314    }
315
316    /// Encodes the GPU commands needed to render all entities in this phase.
317    pub fn render<'w>(
318        &self,
319        render_pass: &mut TrackedRenderPass<'w>,
320        world: &'w World,
321        view: Entity,
322    ) -> Result<(), DrawError> {
323        {
324            let draw_functions = world.resource::<DrawFunctions<BPI>>();
325            let mut draw_functions = draw_functions.write();
326            draw_functions.prepare(world);
327            // Make sure to drop the reader-writer lock here to avoid recursive
328            // locks.
329        }
330
331        self.render_batchable_meshes(render_pass, world, view)?;
332        self.render_unbatchable_meshes(render_pass, world, view)?;
333        self.render_non_meshes(render_pass, world, view)?;
334
335        Ok(())
336    }
337
338    /// Renders all batchable meshes queued in this phase.
339    fn render_batchable_meshes<'w>(
340        &self,
341        render_pass: &mut TrackedRenderPass<'w>,
342        world: &'w World,
343        view: Entity,
344    ) -> Result<(), DrawError> {
345        let draw_functions = world.resource::<DrawFunctions<BPI>>();
346        let mut draw_functions = draw_functions.write();
347
348        debug_assert_eq!(self.batchable_mesh_keys.len(), self.batch_sets.len());
349
350        for (key, batch_set) in self.batchable_mesh_keys.iter().zip(self.batch_sets.iter()) {
351            for batch in batch_set {
352                let binned_phase_item = BPI::new(
353                    key.clone(),
354                    batch.representative_entity,
355                    batch.instance_range.clone(),
356                    batch.extra_index,
357                );
358
359                // Fetch the draw function.
360                let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
361                else {
362                    continue;
363                };
364
365                draw_function.draw(world, render_pass, view, &binned_phase_item)?;
366            }
367        }
368
369        Ok(())
370    }
371
372    /// Renders all unbatchable meshes queued in this phase.
373    fn render_unbatchable_meshes<'w>(
374        &self,
375        render_pass: &mut TrackedRenderPass<'w>,
376        world: &'w World,
377        view: Entity,
378    ) -> Result<(), DrawError> {
379        let draw_functions = world.resource::<DrawFunctions<BPI>>();
380        let mut draw_functions = draw_functions.write();
381
382        for key in &self.unbatchable_mesh_keys {
383            let unbatchable_entities = &self.unbatchable_mesh_values[key];
384            for (entity_index, &entity) in unbatchable_entities.entities.iter().enumerate() {
385                let unbatchable_dynamic_offset = match &unbatchable_entities.buffer_indices {
386                    UnbatchableBinnedEntityIndexSet::NoEntities => {
387                        // Shouldn't happen…
388                        continue;
389                    }
390                    UnbatchableBinnedEntityIndexSet::Sparse {
391                        instance_range,
392                        first_indirect_parameters_index,
393                    } => UnbatchableBinnedEntityIndices {
394                        instance_index: instance_range.start + entity_index as u32,
395                        extra_index: match first_indirect_parameters_index {
396                            None => PhaseItemExtraIndex::NONE,
397                            Some(first_indirect_parameters_index) => {
398                                PhaseItemExtraIndex::indirect_parameters_index(
399                                    u32::from(*first_indirect_parameters_index)
400                                        + entity_index as u32,
401                                )
402                            }
403                        },
404                    },
405                    UnbatchableBinnedEntityIndexSet::Dense(ref dynamic_offsets) => {
406                        dynamic_offsets[entity_index]
407                    }
408                };
409
410                let binned_phase_item = BPI::new(
411                    key.clone(),
412                    entity,
413                    unbatchable_dynamic_offset.instance_index
414                        ..(unbatchable_dynamic_offset.instance_index + 1),
415                    unbatchable_dynamic_offset.extra_index,
416                );
417
418                // Fetch the draw function.
419                let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
420                else {
421                    continue;
422                };
423
424                draw_function.draw(world, render_pass, view, &binned_phase_item)?;
425            }
426        }
427        Ok(())
428    }
429
430    /// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`].
431    ///
432    /// These will have been added by plugins or the application.
433    fn render_non_meshes<'w>(
434        &self,
435        render_pass: &mut TrackedRenderPass<'w>,
436        world: &'w World,
437        view: Entity,
438    ) -> Result<(), DrawError> {
439        let draw_functions = world.resource::<DrawFunctions<BPI>>();
440        let mut draw_functions = draw_functions.write();
441
442        for &(ref key, entity) in &self.non_mesh_items {
443            // Come up with a fake batch range and extra index. The draw
444            // function is expected to manage any sort of batching logic itself.
445            let binned_phase_item = BPI::new(key.clone(), entity, 0..1, PhaseItemExtraIndex(0));
446
447            let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
448            else {
449                continue;
450            };
451
452            draw_function.draw(world, render_pass, view, &binned_phase_item)?;
453        }
454
455        Ok(())
456    }
457
458    pub fn is_empty(&self) -> bool {
459        self.batchable_mesh_keys.is_empty()
460            && self.unbatchable_mesh_keys.is_empty()
461            && self.non_mesh_items.is_empty()
462    }
463
464    pub fn clear(&mut self) {
465        self.batchable_mesh_keys.clear();
466        self.batchable_mesh_values.clear();
467        self.unbatchable_mesh_keys.clear();
468        self.unbatchable_mesh_values.clear();
469        self.non_mesh_items.clear();
470        self.batch_sets.clear();
471    }
472}
473
474impl<BPI> Default for BinnedRenderPhase<BPI>
475where
476    BPI: BinnedPhaseItem,
477{
478    fn default() -> Self {
479        Self {
480            batchable_mesh_keys: vec![],
481            batchable_mesh_values: HashMap::default(),
482            unbatchable_mesh_keys: vec![],
483            unbatchable_mesh_values: HashMap::default(),
484            non_mesh_items: vec![],
485            batch_sets: vec![],
486        }
487    }
488}
489
490impl UnbatchableBinnedEntityIndexSet {
491    /// Returns the [`UnbatchableBinnedEntityIndices`] for the given entity.
492    fn indices_for_entity_index(
493        &self,
494        entity_index: u32,
495    ) -> Option<UnbatchableBinnedEntityIndices> {
496        match self {
497            UnbatchableBinnedEntityIndexSet::NoEntities => None,
498            UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. }
499                if entity_index >= instance_range.len() as u32 =>
500            {
501                None
502            }
503            UnbatchableBinnedEntityIndexSet::Sparse {
504                instance_range,
505                first_indirect_parameters_index: None,
506            } => Some(UnbatchableBinnedEntityIndices {
507                instance_index: instance_range.start + entity_index,
508                extra_index: PhaseItemExtraIndex::NONE,
509            }),
510            UnbatchableBinnedEntityIndexSet::Sparse {
511                instance_range,
512                first_indirect_parameters_index: Some(first_indirect_parameters_index),
513            } => Some(UnbatchableBinnedEntityIndices {
514                instance_index: instance_range.start + entity_index,
515                extra_index: PhaseItemExtraIndex::indirect_parameters_index(
516                    u32::from(*first_indirect_parameters_index) + entity_index,
517                ),
518            }),
519            UnbatchableBinnedEntityIndexSet::Dense(ref indices) => {
520                indices.get(entity_index as usize).copied()
521            }
522        }
523    }
524}
525
526/// A convenient abstraction for adding all the systems necessary for a binned
527/// render phase to the render app.
528///
529/// This is the version used when the pipeline supports GPU preprocessing: e.g.
530/// 3D PBR meshes.
531pub struct BinnedRenderPhasePlugin<BPI, GFBD>(PhantomData<(BPI, GFBD)>)
532where
533    BPI: BinnedPhaseItem,
534    GFBD: GetFullBatchData;
535
536impl<BPI, GFBD> Default for BinnedRenderPhasePlugin<BPI, GFBD>
537where
538    BPI: BinnedPhaseItem,
539    GFBD: GetFullBatchData,
540{
541    fn default() -> Self {
542        Self(PhantomData)
543    }
544}
545
546impl<BPI, GFBD> Plugin for BinnedRenderPhasePlugin<BPI, GFBD>
547where
548    BPI: BinnedPhaseItem,
549    GFBD: GetFullBatchData + Sync + Send + 'static,
550{
551    fn build(&self, app: &mut App) {
552        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
553            return;
554        };
555
556        render_app
557            .init_resource::<ViewBinnedRenderPhases<BPI>>()
558            .add_systems(
559                Render,
560                (
561                    batching::sort_binned_render_phase::<BPI>.in_set(RenderSet::PhaseSort),
562                    (
563                        no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
564                            .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
565                        gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
566                            .run_if(
567                                resource_exists::<
568                                    BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
569                                >,
570                            ),
571                    )
572                        .in_set(RenderSet::PrepareResources),
573                ),
574            );
575    }
576}
577
578/// Stores the rendering instructions for a single phase that sorts items in all
579/// views.
580///
581/// They're cleared out every frame, but storing them in a resource like this
582/// allows us to reuse allocations.
583#[derive(Resource, Deref, DerefMut)]
584pub struct ViewSortedRenderPhases<SPI>(pub EntityHashMap<SortedRenderPhase<SPI>>)
585where
586    SPI: SortedPhaseItem;
587
588impl<SPI> Default for ViewSortedRenderPhases<SPI>
589where
590    SPI: SortedPhaseItem,
591{
592    fn default() -> Self {
593        Self(default())
594    }
595}
596
597impl<SPI> ViewSortedRenderPhases<SPI>
598where
599    SPI: SortedPhaseItem,
600{
601    pub fn insert_or_clear(&mut self, entity: Entity) {
602        match self.entry(entity) {
603            Entry::Occupied(mut entry) => entry.get_mut().clear(),
604            Entry::Vacant(entry) => {
605                entry.insert(default());
606            }
607        }
608    }
609}
610
611/// A convenient abstraction for adding all the systems necessary for a sorted
612/// render phase to the render app.
613///
614/// This is the version used when the pipeline supports GPU preprocessing: e.g.
615/// 3D PBR meshes.
616pub struct SortedRenderPhasePlugin<SPI, GFBD>(PhantomData<(SPI, GFBD)>)
617where
618    SPI: SortedPhaseItem,
619    GFBD: GetFullBatchData;
620
621impl<SPI, GFBD> Default for SortedRenderPhasePlugin<SPI, GFBD>
622where
623    SPI: SortedPhaseItem,
624    GFBD: GetFullBatchData,
625{
626    fn default() -> Self {
627        Self(PhantomData)
628    }
629}
630
631impl<SPI, GFBD> Plugin for SortedRenderPhasePlugin<SPI, GFBD>
632where
633    SPI: SortedPhaseItem + CachedRenderPipelinePhaseItem,
634    GFBD: GetFullBatchData + Sync + Send + 'static,
635{
636    fn build(&self, app: &mut App) {
637        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
638            return;
639        };
640
641        render_app
642            .init_resource::<ViewSortedRenderPhases<SPI>>()
643            .add_systems(
644                Render,
645                (
646                    no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
647                        .run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
648                    gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>.run_if(
649                        resource_exists::<
650                            BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
651                        >,
652                    ),
653                )
654                    .in_set(RenderSet::PrepareResources),
655            );
656    }
657}
658
659impl UnbatchableBinnedEntityIndexSet {
660    /// Adds a new entity to the list of unbatchable binned entities.
661    pub fn add(&mut self, indices: UnbatchableBinnedEntityIndices) {
662        match self {
663            UnbatchableBinnedEntityIndexSet::NoEntities => {
664                if indices.extra_index.is_dynamic_offset() {
665                    // This is the first entity we've seen, and we don't have
666                    // compute shaders. Initialize an array.
667                    *self = UnbatchableBinnedEntityIndexSet::Dense(vec![indices]);
668                } else {
669                    // This is the first entity we've seen, and we have compute
670                    // shaders. Initialize the fast path.
671                    *self = UnbatchableBinnedEntityIndexSet::Sparse {
672                        instance_range: indices.instance_index..indices.instance_index + 1,
673                        first_indirect_parameters_index: indices
674                            .extra_index
675                            .as_indirect_parameters_index()
676                            .and_then(|index| NonMaxU32::try_from(index).ok()),
677                    }
678                }
679            }
680
681            UnbatchableBinnedEntityIndexSet::Sparse {
682                ref mut instance_range,
683                first_indirect_parameters_index,
684            } if instance_range.end == indices.instance_index
685                && ((first_indirect_parameters_index.is_none()
686                    && indices.extra_index == PhaseItemExtraIndex::NONE)
687                    || first_indirect_parameters_index.is_some_and(
688                        |first_indirect_parameters_index| {
689                            Some(
690                                u32::from(first_indirect_parameters_index) + instance_range.end
691                                    - instance_range.start,
692                            ) == indices.extra_index.as_indirect_parameters_index()
693                        },
694                    )) =>
695            {
696                // This is the normal case on non-WebGL 2.
697                instance_range.end += 1;
698            }
699
700            UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. } => {
701                // We thought we were in non-WebGL 2 mode, but we got a dynamic
702                // offset or non-contiguous index anyway. This shouldn't happen,
703                // but let's go ahead and do the sensible thing anyhow: demote
704                // the compressed `NoDynamicOffsets` field to the full
705                // `DynamicOffsets` array.
706                let new_dynamic_offsets = (0..instance_range.len() as u32)
707                    .flat_map(|entity_index| self.indices_for_entity_index(entity_index))
708                    .chain(iter::once(indices))
709                    .collect();
710                *self = UnbatchableBinnedEntityIndexSet::Dense(new_dynamic_offsets);
711            }
712
713            UnbatchableBinnedEntityIndexSet::Dense(ref mut dense_indices) => {
714                dense_indices.push(indices);
715            }
716        }
717    }
718}
719
720/// A collection of all items to be rendered that will be encoded to GPU
721/// commands for a single render phase for a single view.
722///
723/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
724/// They are used to queue entities for rendering.
725/// Multiple phases might be required due to different sorting/batching behaviors
726/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
727/// the rendered texture of the previous phase (e.g. for screen-space reflections).
728/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
729/// The render pass might be reused for multiple phases to reduce GPU overhead.
730///
731/// This flavor of render phase is used only for meshes that need to be sorted
732/// back-to-front, such as transparent meshes. For items that don't need strict
733/// sorting, [`BinnedRenderPhase`] is preferred, for performance.
734pub struct SortedRenderPhase<I>
735where
736    I: SortedPhaseItem,
737{
738    /// The items within this [`SortedRenderPhase`].
739    pub items: Vec<I>,
740}
741
742impl<I> Default for SortedRenderPhase<I>
743where
744    I: SortedPhaseItem,
745{
746    fn default() -> Self {
747        Self { items: Vec::new() }
748    }
749}
750
751impl<I> SortedRenderPhase<I>
752where
753    I: SortedPhaseItem,
754{
755    /// Adds a [`PhaseItem`] to this render phase.
756    #[inline]
757    pub fn add(&mut self, item: I) {
758        self.items.push(item);
759    }
760
761    /// Removes all [`PhaseItem`]s from this render phase.
762    #[inline]
763    pub fn clear(&mut self) {
764        self.items.clear();
765    }
766
767    /// Sorts all of its [`PhaseItem`]s.
768    pub fn sort(&mut self) {
769        I::sort(&mut self.items);
770    }
771
772    /// An [`Iterator`] through the associated [`Entity`] for each [`PhaseItem`] in order.
773    #[inline]
774    pub fn iter_entities(&'_ self) -> impl Iterator<Item = Entity> + '_ {
775        self.items.iter().map(PhaseItem::entity)
776    }
777
778    /// Renders all of its [`PhaseItem`]s using their corresponding draw functions.
779    pub fn render<'w>(
780        &self,
781        render_pass: &mut TrackedRenderPass<'w>,
782        world: &'w World,
783        view: Entity,
784    ) -> Result<(), DrawError> {
785        self.render_range(render_pass, world, view, ..)
786    }
787
788    /// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
789    pub fn render_range<'w>(
790        &self,
791        render_pass: &mut TrackedRenderPass<'w>,
792        world: &'w World,
793        view: Entity,
794        range: impl SliceIndex<[I], Output = [I]>,
795    ) -> Result<(), DrawError> {
796        let items = self
797            .items
798            .get(range)
799            .expect("`Range` provided to `render_range()` is out of bounds");
800
801        let draw_functions = world.resource::<DrawFunctions<I>>();
802        let mut draw_functions = draw_functions.write();
803        draw_functions.prepare(world);
804
805        let mut index = 0;
806        while index < items.len() {
807            let item = &items[index];
808            let batch_range = item.batch_range();
809            if batch_range.is_empty() {
810                index += 1;
811            } else {
812                let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
813                draw_function.draw(world, render_pass, view, item)?;
814                index += batch_range.len();
815            }
816        }
817        Ok(())
818    }
819}
820
821/// An item (entity of the render world) which will be drawn to a texture or the screen,
822/// as part of a render phase.
823///
824/// The data required for rendering an entity is extracted from the main world in the
825/// [`ExtractSchedule`](crate::ExtractSchedule).
826/// Then it has to be queued up for rendering during the [`RenderSet::Queue`],
827/// by adding a corresponding phase item to a render phase.
828/// Afterwards it will be possibly sorted and rendered automatically in the
829/// [`RenderSet::PhaseSort`] and [`RenderSet::Render`], respectively.
830///
831/// `PhaseItem`s come in two flavors: [`BinnedPhaseItem`]s and
832/// [`SortedPhaseItem`]s.
833///
834/// * Binned phase items have a `BinKey` which specifies what bin they're to be
835///     placed in. All items in the same bin are eligible to be batched together.
836///     The `BinKey`s are sorted, but the individual bin items aren't. Binned phase
837///     items are good for opaque meshes, in which the order of rendering isn't
838///     important. Generally, binned phase items are faster than sorted phase items.
839///
840/// * Sorted phase items, on the other hand, are placed into one large buffer
841///     and then sorted all at once. This is needed for transparent meshes, which
842///     have to be sorted back-to-front to render with the painter's algorithm.
843///     These types of phase items are generally slower than binned phase items.
844pub trait PhaseItem: Sized + Send + Sync + 'static {
845    /// Whether or not this `PhaseItem` should be subjected to automatic batching. (Default: `true`)
846    const AUTOMATIC_BATCHING: bool = true;
847
848    /// The corresponding entity that will be drawn.
849    ///
850    /// This is used to fetch the render data of the entity, required by the draw function,
851    /// from the render world .
852    fn entity(&self) -> Entity;
853
854    /// The main world entity represented by this `PhaseItem`.
855    fn main_entity(&self) -> MainEntity;
856
857    /// Specifies the [`Draw`] function used to render the item.
858    fn draw_function(&self) -> DrawFunctionId;
859
860    /// The range of instances that the batch covers. After doing a batched draw, batch range
861    /// length phase items will be skipped. This design is to avoid having to restructure the
862    /// render phase unnecessarily.
863    fn batch_range(&self) -> &Range<u32>;
864    fn batch_range_mut(&mut self) -> &mut Range<u32>;
865
866    /// Returns the [`PhaseItemExtraIndex`].
867    ///
868    /// If present, this is either a dynamic offset or an indirect parameters
869    /// index.
870    fn extra_index(&self) -> PhaseItemExtraIndex;
871
872    /// Returns a pair of mutable references to both the batch range and extra
873    /// index.
874    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex);
875}
876
877/// The "extra index" associated with some [`PhaseItem`]s, alongside the
878/// indirect instance index.
879///
880/// Sometimes phase items require another index in addition to the range of
881/// instances they already have. These can be:
882///
883/// * The *dynamic offset*: a `wgpu` dynamic offset into the uniform buffer of
884///     instance data. This is used on platforms that don't support storage
885///     buffers, to work around uniform buffer size limitations.
886///
887/// * The *indirect parameters index*: an index into the buffer that specifies
888///     the indirect parameters for this [`PhaseItem`]'s drawcall. This is used when
889///     indirect mode is on (as used for GPU culling).
890///
891/// Note that our indirect draw functionality requires storage buffers, so it's
892/// impossible to have both a dynamic offset and an indirect parameters index.
893/// This convenient fact allows us to pack both indices into a single `u32`.
894#[derive(Clone, Copy, PartialEq, Eq, Hash)]
895pub struct PhaseItemExtraIndex(pub u32);
896
897impl Debug for PhaseItemExtraIndex {
898    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
899        if self.is_dynamic_offset() {
900            write!(f, "DynamicOffset({})", self.offset())
901        } else if self.is_indirect_parameters_index() {
902            write!(f, "IndirectParametersIndex({})", self.offset())
903        } else {
904            write!(f, "None")
905        }
906    }
907}
908
909impl PhaseItemExtraIndex {
910    /// The flag that indicates that this index is an indirect parameter. If not
911    /// set, this is a dynamic offset.
912    pub const INDIRECT_PARAMETER_INDEX: u32 = 1 << 31;
913    /// To extract the index from a packed [`PhaseItemExtraIndex`], bitwise-and
914    /// the contents with this value.
915    pub const OFFSET_MASK: u32 = Self::INDIRECT_PARAMETER_INDEX - 1;
916    /// To extract the flag from a packed [`PhaseItemExtraIndex`], bitwise-and
917    /// the contents with this value.
918    pub const FLAGS_MASK: u32 = !Self::OFFSET_MASK;
919
920    /// The special value that indicates that no extra index is present.
921    pub const NONE: PhaseItemExtraIndex = PhaseItemExtraIndex(u32::MAX);
922
923    /// Returns either the indirect parameters index or the dynamic offset,
924    /// depending on which is in use.
925    #[inline]
926    fn offset(&self) -> u32 {
927        self.0 & Self::OFFSET_MASK
928    }
929
930    /// Determines whether this extra index is a dynamic offset.
931    #[inline]
932    fn is_dynamic_offset(&self) -> bool {
933        *self != Self::NONE && (self.0 & Self::INDIRECT_PARAMETER_INDEX) == 0
934    }
935
936    /// Determines whether this extra index is an indirect parameters index.
937    #[inline]
938    fn is_indirect_parameters_index(&self) -> bool {
939        *self != Self::NONE && (self.0 & Self::INDIRECT_PARAMETER_INDEX) != 0
940    }
941
942    /// Packs a indirect parameters index into this extra index.
943    #[inline]
944    pub fn indirect_parameters_index(indirect_parameter_index: u32) -> PhaseItemExtraIndex {
945        // Make sure we didn't overflow.
946        debug_assert_eq!(indirect_parameter_index & Self::FLAGS_MASK, 0);
947        PhaseItemExtraIndex(indirect_parameter_index | Self::INDIRECT_PARAMETER_INDEX)
948    }
949
950    /// Returns either an indirect parameters index or
951    /// [`PhaseItemExtraIndex::NONE`], as appropriate.
952    #[inline]
953    pub fn maybe_indirect_parameters_index(
954        maybe_indirect_parameters_index: Option<NonMaxU32>,
955    ) -> PhaseItemExtraIndex {
956        match maybe_indirect_parameters_index {
957            Some(indirect_parameters_index) => {
958                Self::indirect_parameters_index(indirect_parameters_index.into())
959            }
960            None => PhaseItemExtraIndex::NONE,
961        }
962    }
963
964    /// Packs a dynamic offset into this extra index.
965    #[inline]
966    pub fn dynamic_offset(dynamic_offset: u32) -> PhaseItemExtraIndex {
967        // Make sure we didn't overflow.
968        debug_assert_eq!(dynamic_offset & Self::FLAGS_MASK, 0);
969
970        PhaseItemExtraIndex(dynamic_offset)
971    }
972
973    /// Returns either a dynamic offset or [`PhaseItemExtraIndex::NONE`], as
974    /// appropriate.
975    #[inline]
976    pub fn maybe_dynamic_offset(maybe_dynamic_offset: Option<NonMaxU32>) -> PhaseItemExtraIndex {
977        match maybe_dynamic_offset {
978            Some(dynamic_offset) => Self::dynamic_offset(dynamic_offset.into()),
979            None => PhaseItemExtraIndex::NONE,
980        }
981    }
982
983    /// If this extra index describes a dynamic offset, returns it; otherwise,
984    /// returns `None`.
985    #[inline]
986    pub fn as_dynamic_offset(&self) -> Option<NonMaxU32> {
987        if self.is_dynamic_offset() {
988            NonMaxU32::try_from(self.0 & Self::OFFSET_MASK).ok()
989        } else {
990            None
991        }
992    }
993
994    /// If this extra index describes an indirect parameters index, returns it;
995    /// otherwise, returns `None`.
996    #[inline]
997    pub fn as_indirect_parameters_index(&self) -> Option<u32> {
998        if self.is_indirect_parameters_index() {
999            Some(self.0 & Self::OFFSET_MASK)
1000        } else {
1001            None
1002        }
1003    }
1004}
1005
1006/// Represents phase items that are placed into bins. The `BinKey` specifies
1007/// which bin they're to be placed in. Bin keys are sorted, and items within the
1008/// same bin are eligible to be batched together. The elements within the bins
1009/// aren't themselves sorted.
1010///
1011/// An example of a binned phase item is `Opaque3d`, for which the rendering
1012/// order isn't critical.
1013pub trait BinnedPhaseItem: PhaseItem {
1014    /// The key used for binning [`PhaseItem`]s into bins. Order the members of
1015    /// [`BinnedPhaseItem::BinKey`] by the order of binding for best
1016    /// performance. For example, pipeline id, draw function id, mesh asset id,
1017    /// lowest variable bind group id such as the material bind group id, and
1018    /// its dynamic offsets if any, next bind group and offsets, etc. This
1019    /// reduces the need for rebinding between bins and improves performance.
1020    type BinKey: Clone + Send + Sync + Eq + Ord + Hash;
1021
1022    /// Creates a new binned phase item from the key and per-entity data.
1023    ///
1024    /// Unlike [`SortedPhaseItem`]s, this is generally called "just in time"
1025    /// before rendering. The resulting phase item isn't stored in any data
1026    /// structures, resulting in significant memory savings.
1027    fn new(
1028        key: Self::BinKey,
1029        representative_entity: (Entity, MainEntity),
1030        batch_range: Range<u32>,
1031        extra_index: PhaseItemExtraIndex,
1032    ) -> Self;
1033}
1034
1035/// Represents phase items that must be sorted. The `SortKey` specifies the
1036/// order that these items are drawn in. These are placed into a single array,
1037/// and the array as a whole is then sorted.
1038///
1039/// An example of a sorted phase item is `Transparent3d`, which must be sorted
1040/// back to front in order to correctly render with the painter's algorithm.
1041pub trait SortedPhaseItem: PhaseItem {
1042    /// The type used for ordering the items. The smallest values are drawn first.
1043    /// This order can be calculated using the [`ViewRangefinder3d`],
1044    /// based on the view-space `Z` value of the corresponding view matrix.
1045    type SortKey: Ord;
1046
1047    /// Determines the order in which the items are drawn.
1048    fn sort_key(&self) -> Self::SortKey;
1049
1050    /// Sorts a slice of phase items into render order. Generally if the same type
1051    /// is batched this should use a stable sort like [`slice::sort_by_key`].
1052    /// In almost all other cases, this should not be altered from the default,
1053    /// which uses a unstable sort, as this provides the best balance of CPU and GPU
1054    /// performance.
1055    ///
1056    /// Implementers can optionally not sort the list at all. This is generally advisable if and
1057    /// only if the renderer supports a depth prepass, which is by default not supported by
1058    /// the rest of Bevy's first party rendering crates. Even then, this may have a negative
1059    /// impact on GPU-side performance due to overdraw.
1060    ///
1061    /// It's advised to always profile for performance changes when changing this implementation.
1062    #[inline]
1063    fn sort(items: &mut [Self]) {
1064        items.sort_unstable_by_key(Self::sort_key);
1065    }
1066}
1067
1068/// A [`PhaseItem`] item, that automatically sets the appropriate render pipeline,
1069/// cached in the [`PipelineCache`].
1070///
1071/// You can use the [`SetItemPipeline`] render command to set the pipeline for this item.
1072pub trait CachedRenderPipelinePhaseItem: PhaseItem {
1073    /// The id of the render pipeline, cached in the [`PipelineCache`], that will be used to draw
1074    /// this phase item.
1075    fn cached_pipeline(&self) -> CachedRenderPipelineId;
1076}
1077
1078/// A [`RenderCommand`] that sets the pipeline for the [`CachedRenderPipelinePhaseItem`].
1079pub struct SetItemPipeline;
1080
1081impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
1082    type Param = SRes<PipelineCache>;
1083    type ViewQuery = ();
1084    type ItemQuery = ();
1085    #[inline]
1086    fn render<'w>(
1087        item: &P,
1088        _view: (),
1089        _entity: Option<()>,
1090        pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
1091        pass: &mut TrackedRenderPass<'w>,
1092    ) -> RenderCommandResult {
1093        if let Some(pipeline) = pipeline_cache
1094            .into_inner()
1095            .get_render_pipeline(item.cached_pipeline())
1096        {
1097            pass.set_render_pipeline(pipeline);
1098            RenderCommandResult::Success
1099        } else {
1100            RenderCommandResult::Skip
1101        }
1102    }
1103}
1104
1105/// This system sorts the [`PhaseItem`]s of all [`SortedRenderPhase`]s of this
1106/// type.
1107pub fn sort_phase_system<I>(mut render_phases: ResMut<ViewSortedRenderPhases<I>>)
1108where
1109    I: SortedPhaseItem,
1110{
1111    for phase in render_phases.values_mut() {
1112        phase.sort();
1113    }
1114}
1115
1116impl BinnedRenderPhaseType {
1117    /// Creates the appropriate [`BinnedRenderPhaseType`] for a mesh, given its
1118    /// batchability.
1119    pub fn mesh(batchable: bool) -> BinnedRenderPhaseType {
1120        if batchable {
1121            BinnedRenderPhaseType::BatchableMesh
1122        } else {
1123            BinnedRenderPhaseType::UnbatchableMesh
1124        }
1125    }
1126}