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