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}