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