Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/render_phase/mod.rs
6596 views
1
//! The modular rendering abstraction responsible for queuing, preparing, sorting and drawing
2
//! entities as part of separate render phases.
3
//!
4
//! In Bevy each view (camera, or shadow-casting light, etc.) has one or multiple render phases
5
//! (e.g. opaque, transparent, shadow, etc).
6
//! They are used to queue entities for rendering.
7
//! Multiple phases might be required due to different sorting/batching behaviors
8
//! (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
9
//! the rendered texture of the previous phase (e.g. for screen-space reflections).
10
//!
11
//! To draw an entity, a corresponding [`PhaseItem`] has to be added to one or multiple of these
12
//! render phases for each view that it is visible in.
13
//! This must be done in the [`RenderSystems::Queue`].
14
//! After that the render phase sorts them in the [`RenderSystems::PhaseSort`].
15
//! Finally the items are rendered using a single [`TrackedRenderPass`], during
16
//! the [`RenderSystems::Render`].
17
//!
18
//! Therefore each phase item is assigned a [`Draw`] function.
19
//! These set up the state of the [`TrackedRenderPass`] (i.e. select the
20
//! [`RenderPipeline`](crate::render_resource::RenderPipeline), configure the
21
//! [`BindGroup`](crate::render_resource::BindGroup)s, etc.) and then issue a draw call,
22
//! for the corresponding item.
23
//!
24
//! The [`Draw`] function trait can either be implemented directly or such a function can be
25
//! created by composing multiple [`RenderCommand`]s.
26
27
mod draw;
28
mod draw_state;
29
mod rangefinder;
30
31
use bevy_app::{App, Plugin};
32
use bevy_derive::{Deref, DerefMut};
33
use bevy_ecs::component::Tick;
34
use bevy_ecs::entity::EntityHash;
35
use bevy_platform::collections::{hash_map::Entry, HashMap};
36
use bevy_utils::default;
37
pub use draw::*;
38
pub use draw_state::*;
39
use encase::{internal::WriteInto, ShaderSize};
40
use fixedbitset::{Block, FixedBitSet};
41
use indexmap::IndexMap;
42
use nonmax::NonMaxU32;
43
pub use rangefinder::*;
44
use wgpu::Features;
45
46
use crate::batching::gpu_preprocessing::{
47
GpuPreprocessingMode, GpuPreprocessingSupport, PhaseBatchedInstanceBuffers,
48
PhaseIndirectParametersBuffers,
49
};
50
use crate::renderer::RenderDevice;
51
use crate::sync_world::{MainEntity, MainEntityHashMap};
52
use crate::view::RetainedViewEntity;
53
use crate::RenderDebugFlags;
54
use crate::{
55
batching::{
56
self,
57
gpu_preprocessing::{self, BatchedInstanceBuffers},
58
no_gpu_preprocessing::{self, BatchedInstanceBuffer},
59
GetFullBatchData,
60
},
61
render_resource::{CachedRenderPipelineId, GpuArrayBufferIndex, PipelineCache},
62
Render, RenderApp, RenderSystems,
63
};
64
use bevy_ecs::intern::Interned;
65
use bevy_ecs::{
66
define_label,
67
prelude::*,
68
system::{lifetimeless::SRes, SystemParamItem},
69
};
70
use bevy_render::renderer::RenderAdapterInfo;
71
pub use bevy_render_macros::ShaderLabel;
72
use core::{fmt::Debug, hash::Hash, iter, marker::PhantomData, ops::Range, slice::SliceIndex};
73
use smallvec::SmallVec;
74
use tracing::warn;
75
76
define_label!(
77
#[diagnostic::on_unimplemented(
78
note = "consider annotating `{Self}` with `#[derive(ShaderLabel)]`"
79
)]
80
/// Labels used to uniquely identify types of material shaders
81
ShaderLabel,
82
SHADER_LABEL_INTERNER
83
);
84
85
/// A shorthand for `Interned<dyn RenderSubGraph>`.
86
pub type InternedShaderLabel = Interned<dyn ShaderLabel>;
87
88
pub use bevy_render_macros::DrawFunctionLabel;
89
90
define_label!(
91
#[diagnostic::on_unimplemented(
92
note = "consider annotating `{Self}` with `#[derive(DrawFunctionLabel)]`"
93
)]
94
/// Labels used to uniquely identify types of material shaders
95
DrawFunctionLabel,
96
DRAW_FUNCTION_LABEL_INTERNER
97
);
98
99
pub type InternedDrawFunctionLabel = Interned<dyn DrawFunctionLabel>;
100
101
/// Stores the rendering instructions for a single phase that uses bins in all
102
/// views.
103
///
104
/// They're cleared out every frame, but storing them in a resource like this
105
/// allows us to reuse allocations.
106
#[derive(Resource, Deref, DerefMut)]
107
pub struct ViewBinnedRenderPhases<BPI>(pub HashMap<RetainedViewEntity, BinnedRenderPhase<BPI>>)
108
where
109
BPI: BinnedPhaseItem;
110
111
/// A collection of all rendering instructions, that will be executed by the GPU, for a
112
/// single render phase for a single view.
113
///
114
/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
115
/// They are used to queue entities for rendering.
116
/// Multiple phases might be required due to different sorting/batching behaviors
117
/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
118
/// the rendered texture of the previous phase (e.g. for screen-space reflections).
119
/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
120
/// The render pass might be reused for multiple phases to reduce GPU overhead.
121
///
122
/// This flavor of render phase is used for phases in which the ordering is less
123
/// critical: for example, `Opaque3d`. It's generally faster than the
124
/// alternative [`SortedRenderPhase`].
125
pub struct BinnedRenderPhase<BPI>
126
where
127
BPI: BinnedPhaseItem,
128
{
129
/// The multidrawable bins.
130
///
131
/// Each batch set key maps to a *batch set*, which in this case is a set of
132
/// meshes that can be drawn together in one multidraw call. Each batch set
133
/// is subdivided into *bins*, each of which represents a particular mesh.
134
/// Each bin contains the entity IDs of instances of that mesh.
135
///
136
/// So, for example, if there are two cubes and a sphere present in the
137
/// scene, we would generally have one batch set containing two bins,
138
/// assuming that the cubes and sphere meshes are allocated together and use
139
/// the same pipeline. The first bin, corresponding to the cubes, will have
140
/// two entities in it. The second bin, corresponding to the sphere, will
141
/// have one entity in it.
142
pub multidrawable_meshes: IndexMap<BPI::BatchSetKey, IndexMap<BPI::BinKey, RenderBin>>,
143
144
/// The bins corresponding to batchable items that aren't multidrawable.
145
///
146
/// For multidrawable entities, use `multidrawable_meshes`; for
147
/// unbatchable entities, use `unbatchable_values`.
148
pub batchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
149
150
/// The unbatchable bins.
151
///
152
/// Each entity here is rendered in a separate drawcall.
153
pub unbatchable_meshes: IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
154
155
/// Items in the bin that aren't meshes at all.
156
///
157
/// Bevy itself doesn't place anything in this list, but plugins or your app
158
/// can in order to execute custom drawing commands. Draw functions for each
159
/// entity are simply called in order at rendering time.
160
///
161
/// See the `custom_phase_item` example for an example of how to use this.
162
pub non_mesh_items: IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
163
164
/// Information on each batch set.
165
///
166
/// A *batch set* is a set of entities that will be batched together unless
167
/// we're on a platform that doesn't support storage buffers (e.g. WebGL 2)
168
/// and differing dynamic uniform indices force us to break batches. On
169
/// platforms that support storage buffers, a batch set always consists of
170
/// at most one batch.
171
///
172
/// Multidrawable entities come first, then batchable entities, then
173
/// unbatchable entities.
174
pub(crate) batch_sets: BinnedRenderPhaseBatchSets<BPI::BinKey>,
175
176
/// The batch and bin key for each entity.
177
///
178
/// We retain these so that, when the entity changes,
179
/// [`Self::sweep_old_entities`] can quickly find the bin it was located in
180
/// and remove it.
181
cached_entity_bin_keys: IndexMap<MainEntity, CachedBinnedEntity<BPI>, EntityHash>,
182
183
/// The set of indices in [`Self::cached_entity_bin_keys`] that are
184
/// confirmed to be up to date.
185
///
186
/// Note that each bit in this bit set refers to an *index* in the
187
/// [`IndexMap`] (i.e. a bucket in the hash table). They aren't entity IDs.
188
valid_cached_entity_bin_keys: FixedBitSet,
189
190
/// The set of entities that changed bins this frame.
191
///
192
/// An entity will only be present in this list if it was in one bin on the
193
/// previous frame and is in a new bin on this frame. Each list entry
194
/// specifies the bin the entity used to be in. We use this in order to
195
/// remove the entity from the old bin during
196
/// [`BinnedRenderPhase::sweep_old_entities`].
197
entities_that_changed_bins: Vec<EntityThatChangedBins<BPI>>,
198
/// The gpu preprocessing mode configured for the view this phase is associated
199
/// with.
200
gpu_preprocessing_mode: GpuPreprocessingMode,
201
}
202
203
/// All entities that share a mesh and a material and can be batched as part of
204
/// a [`BinnedRenderPhase`].
205
#[derive(Default)]
206
pub struct RenderBin {
207
/// A list of the entities in each bin, along with their cached
208
/// [`InputUniformIndex`].
209
entities: IndexMap<MainEntity, InputUniformIndex, EntityHash>,
210
}
211
212
/// Information that we track about an entity that was in one bin on the
213
/// previous frame and is in a different bin this frame.
214
struct EntityThatChangedBins<BPI>
215
where
216
BPI: BinnedPhaseItem,
217
{
218
/// The entity.
219
main_entity: MainEntity,
220
/// The key that identifies the bin that this entity used to be in.
221
old_cached_binned_entity: CachedBinnedEntity<BPI>,
222
}
223
224
/// Information that we keep about an entity currently within a bin.
225
pub struct CachedBinnedEntity<BPI>
226
where
227
BPI: BinnedPhaseItem,
228
{
229
/// Information that we use to identify a cached entity in a bin.
230
pub cached_bin_key: Option<CachedBinKey<BPI>>,
231
/// The last modified tick of the entity.
232
///
233
/// We use this to detect when the entity needs to be invalidated.
234
pub change_tick: Tick,
235
}
236
237
/// Information that we use to identify a cached entity in a bin.
238
pub struct CachedBinKey<BPI>
239
where
240
BPI: BinnedPhaseItem,
241
{
242
/// The key of the batch set containing the entity.
243
pub batch_set_key: BPI::BatchSetKey,
244
/// The key of the bin containing the entity.
245
pub bin_key: BPI::BinKey,
246
/// The type of render phase that we use to render the entity: multidraw,
247
/// plain batch, etc.
248
pub phase_type: BinnedRenderPhaseType,
249
}
250
251
impl<BPI> Clone for CachedBinnedEntity<BPI>
252
where
253
BPI: BinnedPhaseItem,
254
{
255
fn clone(&self) -> Self {
256
CachedBinnedEntity {
257
cached_bin_key: self.cached_bin_key.clone(),
258
change_tick: self.change_tick,
259
}
260
}
261
}
262
263
impl<BPI> Clone for CachedBinKey<BPI>
264
where
265
BPI: BinnedPhaseItem,
266
{
267
fn clone(&self) -> Self {
268
CachedBinKey {
269
batch_set_key: self.batch_set_key.clone(),
270
bin_key: self.bin_key.clone(),
271
phase_type: self.phase_type,
272
}
273
}
274
}
275
276
impl<BPI> PartialEq for CachedBinKey<BPI>
277
where
278
BPI: BinnedPhaseItem,
279
{
280
fn eq(&self, other: &Self) -> bool {
281
self.batch_set_key == other.batch_set_key
282
&& self.bin_key == other.bin_key
283
&& self.phase_type == other.phase_type
284
}
285
}
286
287
/// How we store and render the batch sets.
288
///
289
/// Each one of these corresponds to a [`GpuPreprocessingMode`].
290
pub enum BinnedRenderPhaseBatchSets<BK> {
291
/// Batches are grouped into batch sets based on dynamic uniforms.
292
///
293
/// This corresponds to [`GpuPreprocessingMode::None`].
294
DynamicUniforms(Vec<SmallVec<[BinnedRenderPhaseBatch; 1]>>),
295
296
/// Batches are never grouped into batch sets.
297
///
298
/// This corresponds to [`GpuPreprocessingMode::PreprocessingOnly`].
299
Direct(Vec<BinnedRenderPhaseBatch>),
300
301
/// Batches are grouped together into batch sets based on their ability to
302
/// be multi-drawn together.
303
///
304
/// This corresponds to [`GpuPreprocessingMode::Culling`].
305
MultidrawIndirect(Vec<BinnedRenderPhaseBatchSet<BK>>),
306
}
307
308
/// A group of entities that will be batched together into a single multi-draw
309
/// call.
310
pub struct BinnedRenderPhaseBatchSet<BK> {
311
/// The first batch in this batch set.
312
pub(crate) first_batch: BinnedRenderPhaseBatch,
313
/// The key of the bin that the first batch corresponds to.
314
pub(crate) bin_key: BK,
315
/// The number of batches.
316
pub(crate) batch_count: u32,
317
/// The index of the batch set in the GPU buffer.
318
pub(crate) index: u32,
319
}
320
321
impl<BK> BinnedRenderPhaseBatchSets<BK> {
322
fn clear(&mut self) {
323
match *self {
324
BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut vec) => vec.clear(),
325
BinnedRenderPhaseBatchSets::Direct(ref mut vec) => vec.clear(),
326
BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => vec.clear(),
327
}
328
}
329
}
330
331
/// Information about a single batch of entities rendered using binned phase
332
/// items.
333
#[derive(Debug)]
334
pub struct BinnedRenderPhaseBatch {
335
/// An entity that's *representative* of this batch.
336
///
337
/// Bevy uses this to fetch the mesh. It can be any entity in the batch.
338
pub representative_entity: (Entity, MainEntity),
339
/// The range of instance indices in this batch.
340
pub instance_range: Range<u32>,
341
342
/// The dynamic offset of the batch.
343
///
344
/// Note that dynamic offsets are only used on platforms that don't support
345
/// storage buffers.
346
pub extra_index: PhaseItemExtraIndex,
347
}
348
349
/// Information about the unbatchable entities in a bin.
350
pub struct UnbatchableBinnedEntities {
351
/// The entities.
352
pub entities: MainEntityHashMap<Entity>,
353
354
/// The GPU array buffer indices of each unbatchable binned entity.
355
pub(crate) buffer_indices: UnbatchableBinnedEntityIndexSet,
356
}
357
358
/// Information about [`BinnedRenderPhaseType::NonMesh`] entities.
359
pub struct NonMeshEntities {
360
/// The entities.
361
pub entities: MainEntityHashMap<Entity>,
362
}
363
364
/// Stores instance indices and dynamic offsets for unbatchable entities in a
365
/// binned render phase.
366
///
367
/// This is conceptually `Vec<UnbatchableBinnedEntityDynamicOffset>`, but it
368
/// avoids the overhead of storing dynamic offsets on platforms that support
369
/// them. In other words, this allows a fast path that avoids allocation on
370
/// platforms that aren't WebGL 2.
371
#[derive(Default)]
372
373
pub(crate) enum UnbatchableBinnedEntityIndexSet {
374
/// There are no unbatchable entities in this bin (yet).
375
#[default]
376
NoEntities,
377
378
/// The instances for all unbatchable entities in this bin are contiguous,
379
/// and there are no dynamic uniforms.
380
///
381
/// This is the typical case on platforms other than WebGL 2. We special
382
/// case this to avoid allocation on those platforms.
383
Sparse {
384
/// The range of indices.
385
instance_range: Range<u32>,
386
/// The index of the first indirect instance parameters.
387
///
388
/// The other indices immediately follow these.
389
first_indirect_parameters_index: Option<NonMaxU32>,
390
},
391
392
/// Dynamic uniforms are present for unbatchable entities in this bin.
393
///
394
/// We fall back to this on WebGL 2.
395
Dense(Vec<UnbatchableBinnedEntityIndices>),
396
}
397
398
/// The instance index and dynamic offset (if present) for an unbatchable entity.
399
///
400
/// This is only useful on platforms that don't support storage buffers.
401
#[derive(Clone)]
402
pub(crate) struct UnbatchableBinnedEntityIndices {
403
/// The instance index.
404
pub(crate) instance_index: u32,
405
/// The [`PhaseItemExtraIndex`], if present.
406
pub(crate) extra_index: PhaseItemExtraIndex,
407
}
408
409
/// Identifies the list within [`BinnedRenderPhase`] that a phase item is to be
410
/// placed in.
411
#[derive(Clone, Copy, PartialEq, Debug)]
412
pub enum BinnedRenderPhaseType {
413
/// The item is a mesh that's eligible for multi-draw indirect rendering and
414
/// can be batched with other meshes of the same type.
415
MultidrawableMesh,
416
417
/// The item is a mesh that can be batched with other meshes of the same type and
418
/// drawn in a single draw call.
419
BatchableMesh,
420
421
/// The item is a mesh that's eligible for indirect rendering, but can't be
422
/// batched with other meshes of the same type.
423
UnbatchableMesh,
424
425
/// The item isn't a mesh at all.
426
///
427
/// Bevy will simply invoke the drawing commands for such items one after
428
/// another, with no further processing.
429
///
430
/// The engine itself doesn't enqueue any items of this type, but it's
431
/// available for use in your application and/or plugins.
432
NonMesh,
433
}
434
435
impl<T> From<GpuArrayBufferIndex<T>> for UnbatchableBinnedEntityIndices
436
where
437
T: Clone + ShaderSize + WriteInto,
438
{
439
fn from(value: GpuArrayBufferIndex<T>) -> Self {
440
UnbatchableBinnedEntityIndices {
441
instance_index: value.index,
442
extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(value.dynamic_offset),
443
}
444
}
445
}
446
447
impl<BPI> Default for ViewBinnedRenderPhases<BPI>
448
where
449
BPI: BinnedPhaseItem,
450
{
451
fn default() -> Self {
452
Self(default())
453
}
454
}
455
456
impl<BPI> ViewBinnedRenderPhases<BPI>
457
where
458
BPI: BinnedPhaseItem,
459
{
460
pub fn prepare_for_new_frame(
461
&mut self,
462
retained_view_entity: RetainedViewEntity,
463
gpu_preprocessing: GpuPreprocessingMode,
464
) {
465
match self.entry(retained_view_entity) {
466
Entry::Occupied(mut entry) => entry.get_mut().prepare_for_new_frame(),
467
Entry::Vacant(entry) => {
468
entry.insert(BinnedRenderPhase::<BPI>::new(gpu_preprocessing));
469
}
470
}
471
}
472
}
473
474
/// The index of the uniform describing this object in the GPU buffer, when GPU
475
/// preprocessing is enabled.
476
///
477
/// For example, for 3D meshes, this is the index of the `MeshInputUniform` in
478
/// the buffer.
479
///
480
/// This field is ignored if GPU preprocessing isn't in use, such as (currently)
481
/// in the case of 2D meshes. In that case, it can be safely set to
482
/// [`core::default::Default::default`].
483
#[derive(Clone, Copy, PartialEq, Default, Deref, DerefMut)]
484
#[repr(transparent)]
485
pub struct InputUniformIndex(pub u32);
486
487
impl<BPI> BinnedRenderPhase<BPI>
488
where
489
BPI: BinnedPhaseItem,
490
{
491
/// Bins a new entity.
492
///
493
/// The `phase_type` parameter specifies whether the entity is a
494
/// preprocessable mesh and whether it can be binned with meshes of the same
495
/// type.
496
pub fn add(
497
&mut self,
498
batch_set_key: BPI::BatchSetKey,
499
bin_key: BPI::BinKey,
500
(entity, main_entity): (Entity, MainEntity),
501
input_uniform_index: InputUniformIndex,
502
mut phase_type: BinnedRenderPhaseType,
503
change_tick: Tick,
504
) {
505
// If the user has overridden indirect drawing for this view, we need to
506
// force the phase type to be batchable instead.
507
if self.gpu_preprocessing_mode == GpuPreprocessingMode::PreprocessingOnly
508
&& phase_type == BinnedRenderPhaseType::MultidrawableMesh
509
{
510
phase_type = BinnedRenderPhaseType::BatchableMesh;
511
}
512
513
match phase_type {
514
BinnedRenderPhaseType::MultidrawableMesh => {
515
match self.multidrawable_meshes.entry(batch_set_key.clone()) {
516
indexmap::map::Entry::Occupied(mut entry) => {
517
entry
518
.get_mut()
519
.entry(bin_key.clone())
520
.or_default()
521
.insert(main_entity, input_uniform_index);
522
}
523
indexmap::map::Entry::Vacant(entry) => {
524
let mut new_batch_set = IndexMap::default();
525
new_batch_set.insert(
526
bin_key.clone(),
527
RenderBin::from_entity(main_entity, input_uniform_index),
528
);
529
entry.insert(new_batch_set);
530
}
531
}
532
}
533
534
BinnedRenderPhaseType::BatchableMesh => {
535
match self
536
.batchable_meshes
537
.entry((batch_set_key.clone(), bin_key.clone()).clone())
538
{
539
indexmap::map::Entry::Occupied(mut entry) => {
540
entry.get_mut().insert(main_entity, input_uniform_index);
541
}
542
indexmap::map::Entry::Vacant(entry) => {
543
entry.insert(RenderBin::from_entity(main_entity, input_uniform_index));
544
}
545
}
546
}
547
548
BinnedRenderPhaseType::UnbatchableMesh => {
549
match self
550
.unbatchable_meshes
551
.entry((batch_set_key.clone(), bin_key.clone()))
552
{
553
indexmap::map::Entry::Occupied(mut entry) => {
554
entry.get_mut().entities.insert(main_entity, entity);
555
}
556
indexmap::map::Entry::Vacant(entry) => {
557
let mut entities = MainEntityHashMap::default();
558
entities.insert(main_entity, entity);
559
entry.insert(UnbatchableBinnedEntities {
560
entities,
561
buffer_indices: default(),
562
});
563
}
564
}
565
}
566
567
BinnedRenderPhaseType::NonMesh => {
568
// We don't process these items further.
569
match self
570
.non_mesh_items
571
.entry((batch_set_key.clone(), bin_key.clone()).clone())
572
{
573
indexmap::map::Entry::Occupied(mut entry) => {
574
entry.get_mut().entities.insert(main_entity, entity);
575
}
576
indexmap::map::Entry::Vacant(entry) => {
577
let mut entities = MainEntityHashMap::default();
578
entities.insert(main_entity, entity);
579
entry.insert(NonMeshEntities { entities });
580
}
581
}
582
}
583
}
584
585
// Update the cache.
586
self.update_cache(
587
main_entity,
588
Some(CachedBinKey {
589
batch_set_key,
590
bin_key,
591
phase_type,
592
}),
593
change_tick,
594
);
595
}
596
597
/// Inserts an entity into the cache with the given change tick.
598
pub fn update_cache(
599
&mut self,
600
main_entity: MainEntity,
601
cached_bin_key: Option<CachedBinKey<BPI>>,
602
change_tick: Tick,
603
) {
604
let new_cached_binned_entity = CachedBinnedEntity {
605
cached_bin_key,
606
change_tick,
607
};
608
609
let (index, old_cached_binned_entity) = self
610
.cached_entity_bin_keys
611
.insert_full(main_entity, new_cached_binned_entity.clone());
612
613
// If the entity changed bins, record its old bin so that we can remove
614
// the entity from it.
615
if let Some(old_cached_binned_entity) = old_cached_binned_entity
616
&& old_cached_binned_entity.cached_bin_key != new_cached_binned_entity.cached_bin_key
617
{
618
self.entities_that_changed_bins.push(EntityThatChangedBins {
619
main_entity,
620
old_cached_binned_entity,
621
});
622
}
623
624
// Mark the entity as valid.
625
self.valid_cached_entity_bin_keys.grow_and_insert(index);
626
}
627
628
/// Encodes the GPU commands needed to render all entities in this phase.
629
pub fn render<'w>(
630
&self,
631
render_pass: &mut TrackedRenderPass<'w>,
632
world: &'w World,
633
view: Entity,
634
) -> Result<(), DrawError> {
635
{
636
let draw_functions = world.resource::<DrawFunctions<BPI>>();
637
let mut draw_functions = draw_functions.write();
638
draw_functions.prepare(world);
639
// Make sure to drop the reader-writer lock here to avoid recursive
640
// locks.
641
}
642
643
self.render_batchable_meshes(render_pass, world, view)?;
644
self.render_unbatchable_meshes(render_pass, world, view)?;
645
self.render_non_meshes(render_pass, world, view)?;
646
647
Ok(())
648
}
649
650
/// Renders all batchable meshes queued in this phase.
651
fn render_batchable_meshes<'w>(
652
&self,
653
render_pass: &mut TrackedRenderPass<'w>,
654
world: &'w World,
655
view: Entity,
656
) -> Result<(), DrawError> {
657
let draw_functions = world.resource::<DrawFunctions<BPI>>();
658
let mut draw_functions = draw_functions.write();
659
660
let render_device = world.resource::<RenderDevice>();
661
let render_adapter_info = world.resource::<RenderAdapterInfo>();
662
let multi_draw_indirect_count_supported = render_device
663
.features()
664
.contains(Features::MULTI_DRAW_INDIRECT_COUNT)
665
// TODO: https://github.com/gfx-rs/wgpu/issues/7974
666
&& !matches!(render_adapter_info.backend, wgpu::Backend::Dx12);
667
668
match self.batch_sets {
669
BinnedRenderPhaseBatchSets::DynamicUniforms(ref batch_sets) => {
670
debug_assert_eq!(self.batchable_meshes.len(), batch_sets.len());
671
672
for ((batch_set_key, bin_key), batch_set) in
673
self.batchable_meshes.keys().zip(batch_sets.iter())
674
{
675
for batch in batch_set {
676
let binned_phase_item = BPI::new(
677
batch_set_key.clone(),
678
bin_key.clone(),
679
batch.representative_entity,
680
batch.instance_range.clone(),
681
batch.extra_index.clone(),
682
);
683
684
// Fetch the draw function.
685
let Some(draw_function) =
686
draw_functions.get_mut(binned_phase_item.draw_function())
687
else {
688
continue;
689
};
690
691
draw_function.draw(world, render_pass, view, &binned_phase_item)?;
692
}
693
}
694
}
695
696
BinnedRenderPhaseBatchSets::Direct(ref batch_set) => {
697
for (batch, (batch_set_key, bin_key)) in
698
batch_set.iter().zip(self.batchable_meshes.keys())
699
{
700
let binned_phase_item = BPI::new(
701
batch_set_key.clone(),
702
bin_key.clone(),
703
batch.representative_entity,
704
batch.instance_range.clone(),
705
batch.extra_index.clone(),
706
);
707
708
// Fetch the draw function.
709
let Some(draw_function) =
710
draw_functions.get_mut(binned_phase_item.draw_function())
711
else {
712
continue;
713
};
714
715
draw_function.draw(world, render_pass, view, &binned_phase_item)?;
716
}
717
}
718
719
BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) => {
720
for (batch_set_key, batch_set) in self
721
.multidrawable_meshes
722
.keys()
723
.chain(
724
self.batchable_meshes
725
.keys()
726
.map(|(batch_set_key, _)| batch_set_key),
727
)
728
.zip(batch_sets.iter())
729
{
730
let batch = &batch_set.first_batch;
731
732
let batch_set_index = if multi_draw_indirect_count_supported {
733
NonMaxU32::new(batch_set.index)
734
} else {
735
None
736
};
737
738
let binned_phase_item = BPI::new(
739
batch_set_key.clone(),
740
batch_set.bin_key.clone(),
741
batch.representative_entity,
742
batch.instance_range.clone(),
743
match batch.extra_index {
744
PhaseItemExtraIndex::None => PhaseItemExtraIndex::None,
745
PhaseItemExtraIndex::DynamicOffset(ref dynamic_offset) => {
746
PhaseItemExtraIndex::DynamicOffset(*dynamic_offset)
747
}
748
PhaseItemExtraIndex::IndirectParametersIndex { ref range, .. } => {
749
PhaseItemExtraIndex::IndirectParametersIndex {
750
range: range.start..(range.start + batch_set.batch_count),
751
batch_set_index,
752
}
753
}
754
},
755
);
756
757
// Fetch the draw function.
758
let Some(draw_function) =
759
draw_functions.get_mut(binned_phase_item.draw_function())
760
else {
761
continue;
762
};
763
764
draw_function.draw(world, render_pass, view, &binned_phase_item)?;
765
}
766
}
767
}
768
769
Ok(())
770
}
771
772
/// Renders all unbatchable meshes queued in this phase.
773
fn render_unbatchable_meshes<'w>(
774
&self,
775
render_pass: &mut TrackedRenderPass<'w>,
776
world: &'w World,
777
view: Entity,
778
) -> Result<(), DrawError> {
779
let draw_functions = world.resource::<DrawFunctions<BPI>>();
780
let mut draw_functions = draw_functions.write();
781
782
for (batch_set_key, bin_key) in self.unbatchable_meshes.keys() {
783
let unbatchable_entities =
784
&self.unbatchable_meshes[&(batch_set_key.clone(), bin_key.clone())];
785
for (entity_index, entity) in unbatchable_entities.entities.iter().enumerate() {
786
let unbatchable_dynamic_offset = match &unbatchable_entities.buffer_indices {
787
UnbatchableBinnedEntityIndexSet::NoEntities => {
788
// Shouldn't happen…
789
continue;
790
}
791
UnbatchableBinnedEntityIndexSet::Sparse {
792
instance_range,
793
first_indirect_parameters_index,
794
} => UnbatchableBinnedEntityIndices {
795
instance_index: instance_range.start + entity_index as u32,
796
extra_index: match first_indirect_parameters_index {
797
None => PhaseItemExtraIndex::None,
798
Some(first_indirect_parameters_index) => {
799
let first_indirect_parameters_index_for_entity =
800
u32::from(*first_indirect_parameters_index)
801
+ entity_index as u32;
802
PhaseItemExtraIndex::IndirectParametersIndex {
803
range: first_indirect_parameters_index_for_entity
804
..(first_indirect_parameters_index_for_entity + 1),
805
batch_set_index: None,
806
}
807
}
808
},
809
},
810
UnbatchableBinnedEntityIndexSet::Dense(dynamic_offsets) => {
811
dynamic_offsets[entity_index].clone()
812
}
813
};
814
815
let binned_phase_item = BPI::new(
816
batch_set_key.clone(),
817
bin_key.clone(),
818
(*entity.1, *entity.0),
819
unbatchable_dynamic_offset.instance_index
820
..(unbatchable_dynamic_offset.instance_index + 1),
821
unbatchable_dynamic_offset.extra_index,
822
);
823
824
// Fetch the draw function.
825
let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
826
else {
827
continue;
828
};
829
830
draw_function.draw(world, render_pass, view, &binned_phase_item)?;
831
}
832
}
833
Ok(())
834
}
835
836
/// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`].
837
///
838
/// These will have been added by plugins or the application.
839
fn render_non_meshes<'w>(
840
&self,
841
render_pass: &mut TrackedRenderPass<'w>,
842
world: &'w World,
843
view: Entity,
844
) -> Result<(), DrawError> {
845
let draw_functions = world.resource::<DrawFunctions<BPI>>();
846
let mut draw_functions = draw_functions.write();
847
848
for ((batch_set_key, bin_key), non_mesh_entities) in &self.non_mesh_items {
849
for (main_entity, entity) in non_mesh_entities.entities.iter() {
850
// Come up with a fake batch range and extra index. The draw
851
// function is expected to manage any sort of batching logic itself.
852
let binned_phase_item = BPI::new(
853
batch_set_key.clone(),
854
bin_key.clone(),
855
(*entity, *main_entity),
856
0..1,
857
PhaseItemExtraIndex::None,
858
);
859
860
let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
861
else {
862
continue;
863
};
864
865
draw_function.draw(world, render_pass, view, &binned_phase_item)?;
866
}
867
}
868
869
Ok(())
870
}
871
872
pub fn is_empty(&self) -> bool {
873
self.multidrawable_meshes.is_empty()
874
&& self.batchable_meshes.is_empty()
875
&& self.unbatchable_meshes.is_empty()
876
&& self.non_mesh_items.is_empty()
877
}
878
879
pub fn prepare_for_new_frame(&mut self) {
880
self.batch_sets.clear();
881
882
self.valid_cached_entity_bin_keys.clear();
883
self.valid_cached_entity_bin_keys
884
.grow(self.cached_entity_bin_keys.len());
885
self.valid_cached_entity_bin_keys
886
.set_range(self.cached_entity_bin_keys.len().., true);
887
888
self.entities_that_changed_bins.clear();
889
890
for unbatchable_bin in self.unbatchable_meshes.values_mut() {
891
unbatchable_bin.buffer_indices.clear();
892
}
893
}
894
895
/// Checks to see whether the entity is in a bin and returns true if it's
896
/// both in a bin and up to date.
897
///
898
/// If this function returns true, we also add the entry to the
899
/// `valid_cached_entity_bin_keys` list.
900
pub fn validate_cached_entity(
901
&mut self,
902
visible_entity: MainEntity,
903
current_change_tick: Tick,
904
) -> bool {
905
if let indexmap::map::Entry::Occupied(entry) =
906
self.cached_entity_bin_keys.entry(visible_entity)
907
&& entry.get().change_tick == current_change_tick
908
{
909
self.valid_cached_entity_bin_keys.insert(entry.index());
910
return true;
911
}
912
913
false
914
}
915
916
/// Removes all entities not marked as clean from the bins.
917
///
918
/// During `queue_material_meshes`, we process all visible entities and mark
919
/// each as clean as we come to it. Then, in [`sweep_old_entities`], we call
920
/// this method, which removes entities that aren't marked as clean from the
921
/// bins.
922
pub fn sweep_old_entities(&mut self) {
923
// Search for entities not marked as valid. We have to do this in
924
// reverse order because `swap_remove_index` will potentially invalidate
925
// all indices after the one we remove.
926
for index in ReverseFixedBitSetZeroesIterator::new(&self.valid_cached_entity_bin_keys) {
927
let Some((entity, cached_binned_entity)) =
928
self.cached_entity_bin_keys.swap_remove_index(index)
929
else {
930
continue;
931
};
932
933
if let Some(ref cached_bin_key) = cached_binned_entity.cached_bin_key {
934
remove_entity_from_bin(
935
entity,
936
cached_bin_key,
937
&mut self.multidrawable_meshes,
938
&mut self.batchable_meshes,
939
&mut self.unbatchable_meshes,
940
&mut self.non_mesh_items,
941
);
942
}
943
}
944
945
// If an entity changed bins, we need to remove it from its old bin.
946
for entity_that_changed_bins in self.entities_that_changed_bins.drain(..) {
947
let Some(ref old_cached_bin_key) = entity_that_changed_bins
948
.old_cached_binned_entity
949
.cached_bin_key
950
else {
951
continue;
952
};
953
remove_entity_from_bin(
954
entity_that_changed_bins.main_entity,
955
old_cached_bin_key,
956
&mut self.multidrawable_meshes,
957
&mut self.batchable_meshes,
958
&mut self.unbatchable_meshes,
959
&mut self.non_mesh_items,
960
);
961
}
962
}
963
}
964
965
/// Removes an entity from a bin.
966
///
967
/// If this makes the bin empty, this function removes the bin as well.
968
///
969
/// This is a standalone function instead of a method on [`BinnedRenderPhase`]
970
/// for borrow check reasons.
971
fn remove_entity_from_bin<BPI>(
972
entity: MainEntity,
973
entity_bin_key: &CachedBinKey<BPI>,
974
multidrawable_meshes: &mut IndexMap<BPI::BatchSetKey, IndexMap<BPI::BinKey, RenderBin>>,
975
batchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), RenderBin>,
976
unbatchable_meshes: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), UnbatchableBinnedEntities>,
977
non_mesh_items: &mut IndexMap<(BPI::BatchSetKey, BPI::BinKey), NonMeshEntities>,
978
) where
979
BPI: BinnedPhaseItem,
980
{
981
match entity_bin_key.phase_type {
982
BinnedRenderPhaseType::MultidrawableMesh => {
983
if let indexmap::map::Entry::Occupied(mut batch_set_entry) =
984
multidrawable_meshes.entry(entity_bin_key.batch_set_key.clone())
985
{
986
if let indexmap::map::Entry::Occupied(mut bin_entry) = batch_set_entry
987
.get_mut()
988
.entry(entity_bin_key.bin_key.clone())
989
{
990
bin_entry.get_mut().remove(entity);
991
992
// If the bin is now empty, remove the bin.
993
if bin_entry.get_mut().is_empty() {
994
bin_entry.swap_remove();
995
}
996
}
997
998
// If the batch set is now empty, remove it. This will perturb
999
// the order, but that's OK because we're going to sort the bin
1000
// afterwards.
1001
if batch_set_entry.get_mut().is_empty() {
1002
batch_set_entry.swap_remove();
1003
}
1004
}
1005
}
1006
1007
BinnedRenderPhaseType::BatchableMesh => {
1008
if let indexmap::map::Entry::Occupied(mut bin_entry) = batchable_meshes.entry((
1009
entity_bin_key.batch_set_key.clone(),
1010
entity_bin_key.bin_key.clone(),
1011
)) {
1012
bin_entry.get_mut().remove(entity);
1013
1014
// If the bin is now empty, remove the bin.
1015
if bin_entry.get_mut().is_empty() {
1016
bin_entry.swap_remove();
1017
}
1018
}
1019
}
1020
1021
BinnedRenderPhaseType::UnbatchableMesh => {
1022
if let indexmap::map::Entry::Occupied(mut bin_entry) = unbatchable_meshes.entry((
1023
entity_bin_key.batch_set_key.clone(),
1024
entity_bin_key.bin_key.clone(),
1025
)) {
1026
bin_entry.get_mut().entities.remove(&entity);
1027
1028
// If the bin is now empty, remove the bin.
1029
if bin_entry.get_mut().entities.is_empty() {
1030
bin_entry.swap_remove();
1031
}
1032
}
1033
}
1034
1035
BinnedRenderPhaseType::NonMesh => {
1036
if let indexmap::map::Entry::Occupied(mut bin_entry) = non_mesh_items.entry((
1037
entity_bin_key.batch_set_key.clone(),
1038
entity_bin_key.bin_key.clone(),
1039
)) {
1040
bin_entry.get_mut().entities.remove(&entity);
1041
1042
// If the bin is now empty, remove the bin.
1043
if bin_entry.get_mut().entities.is_empty() {
1044
bin_entry.swap_remove();
1045
}
1046
}
1047
}
1048
}
1049
}
1050
1051
impl<BPI> BinnedRenderPhase<BPI>
1052
where
1053
BPI: BinnedPhaseItem,
1054
{
1055
fn new(gpu_preprocessing: GpuPreprocessingMode) -> Self {
1056
Self {
1057
multidrawable_meshes: IndexMap::default(),
1058
batchable_meshes: IndexMap::default(),
1059
unbatchable_meshes: IndexMap::default(),
1060
non_mesh_items: IndexMap::default(),
1061
batch_sets: match gpu_preprocessing {
1062
GpuPreprocessingMode::Culling => {
1063
BinnedRenderPhaseBatchSets::MultidrawIndirect(vec![])
1064
}
1065
GpuPreprocessingMode::PreprocessingOnly => {
1066
BinnedRenderPhaseBatchSets::Direct(vec![])
1067
}
1068
GpuPreprocessingMode::None => BinnedRenderPhaseBatchSets::DynamicUniforms(vec![]),
1069
},
1070
cached_entity_bin_keys: IndexMap::default(),
1071
valid_cached_entity_bin_keys: FixedBitSet::new(),
1072
entities_that_changed_bins: vec![],
1073
gpu_preprocessing_mode: gpu_preprocessing,
1074
}
1075
}
1076
}
1077
1078
impl UnbatchableBinnedEntityIndexSet {
1079
/// Returns the [`UnbatchableBinnedEntityIndices`] for the given entity.
1080
fn indices_for_entity_index(
1081
&self,
1082
entity_index: u32,
1083
) -> Option<UnbatchableBinnedEntityIndices> {
1084
match self {
1085
UnbatchableBinnedEntityIndexSet::NoEntities => None,
1086
UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. }
1087
if entity_index >= instance_range.len() as u32 =>
1088
{
1089
None
1090
}
1091
UnbatchableBinnedEntityIndexSet::Sparse {
1092
instance_range,
1093
first_indirect_parameters_index: None,
1094
} => Some(UnbatchableBinnedEntityIndices {
1095
instance_index: instance_range.start + entity_index,
1096
extra_index: PhaseItemExtraIndex::None,
1097
}),
1098
UnbatchableBinnedEntityIndexSet::Sparse {
1099
instance_range,
1100
first_indirect_parameters_index: Some(first_indirect_parameters_index),
1101
} => {
1102
let first_indirect_parameters_index_for_this_batch =
1103
u32::from(*first_indirect_parameters_index) + entity_index;
1104
Some(UnbatchableBinnedEntityIndices {
1105
instance_index: instance_range.start + entity_index,
1106
extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1107
range: first_indirect_parameters_index_for_this_batch
1108
..(first_indirect_parameters_index_for_this_batch + 1),
1109
batch_set_index: None,
1110
},
1111
})
1112
}
1113
UnbatchableBinnedEntityIndexSet::Dense(indices) => {
1114
indices.get(entity_index as usize).cloned()
1115
}
1116
}
1117
}
1118
}
1119
1120
/// A convenient abstraction for adding all the systems necessary for a binned
1121
/// render phase to the render app.
1122
///
1123
/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1124
/// 3D PBR meshes.
1125
pub struct BinnedRenderPhasePlugin<BPI, GFBD>
1126
where
1127
BPI: BinnedPhaseItem,
1128
GFBD: GetFullBatchData,
1129
{
1130
/// Debugging flags that can optionally be set when constructing the renderer.
1131
pub debug_flags: RenderDebugFlags,
1132
phantom: PhantomData<(BPI, GFBD)>,
1133
}
1134
1135
impl<BPI, GFBD> BinnedRenderPhasePlugin<BPI, GFBD>
1136
where
1137
BPI: BinnedPhaseItem,
1138
GFBD: GetFullBatchData,
1139
{
1140
pub fn new(debug_flags: RenderDebugFlags) -> Self {
1141
Self {
1142
debug_flags,
1143
phantom: PhantomData,
1144
}
1145
}
1146
}
1147
1148
impl<BPI, GFBD> Plugin for BinnedRenderPhasePlugin<BPI, GFBD>
1149
where
1150
BPI: BinnedPhaseItem,
1151
GFBD: GetFullBatchData + Sync + Send + 'static,
1152
{
1153
fn build(&self, app: &mut App) {
1154
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1155
return;
1156
};
1157
1158
render_app
1159
.init_resource::<ViewBinnedRenderPhases<BPI>>()
1160
.init_resource::<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>()
1161
.insert_resource(PhaseIndirectParametersBuffers::<BPI>::new(
1162
self.debug_flags
1163
.contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
1164
))
1165
.add_systems(
1166
Render,
1167
(
1168
batching::sort_binned_render_phase::<BPI>.in_set(RenderSystems::PhaseSort),
1169
(
1170
no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1171
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1172
gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
1173
.run_if(
1174
resource_exists::<
1175
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1176
>,
1177
),
1178
)
1179
.in_set(RenderSystems::PrepareResources),
1180
sweep_old_entities::<BPI>.in_set(RenderSystems::QueueSweep),
1181
gpu_preprocessing::collect_buffers_for_phase::<BPI, GFBD>
1182
.run_if(
1183
resource_exists::<
1184
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1185
>,
1186
)
1187
.in_set(RenderSystems::PrepareResourcesCollectPhaseBuffers),
1188
),
1189
);
1190
}
1191
}
1192
1193
/// Stores the rendering instructions for a single phase that sorts items in all
1194
/// views.
1195
///
1196
/// They're cleared out every frame, but storing them in a resource like this
1197
/// allows us to reuse allocations.
1198
#[derive(Resource, Deref, DerefMut)]
1199
pub struct ViewSortedRenderPhases<SPI>(pub HashMap<RetainedViewEntity, SortedRenderPhase<SPI>>)
1200
where
1201
SPI: SortedPhaseItem;
1202
1203
impl<SPI> Default for ViewSortedRenderPhases<SPI>
1204
where
1205
SPI: SortedPhaseItem,
1206
{
1207
fn default() -> Self {
1208
Self(default())
1209
}
1210
}
1211
1212
impl<SPI> ViewSortedRenderPhases<SPI>
1213
where
1214
SPI: SortedPhaseItem,
1215
{
1216
pub fn insert_or_clear(&mut self, retained_view_entity: RetainedViewEntity) {
1217
match self.entry(retained_view_entity) {
1218
Entry::Occupied(mut entry) => entry.get_mut().clear(),
1219
Entry::Vacant(entry) => {
1220
entry.insert(default());
1221
}
1222
}
1223
}
1224
}
1225
1226
/// A convenient abstraction for adding all the systems necessary for a sorted
1227
/// render phase to the render app.
1228
///
1229
/// This is the version used when the pipeline supports GPU preprocessing: e.g.
1230
/// 3D PBR meshes.
1231
pub struct SortedRenderPhasePlugin<SPI, GFBD>
1232
where
1233
SPI: SortedPhaseItem,
1234
GFBD: GetFullBatchData,
1235
{
1236
/// Debugging flags that can optionally be set when constructing the renderer.
1237
pub debug_flags: RenderDebugFlags,
1238
phantom: PhantomData<(SPI, GFBD)>,
1239
}
1240
1241
impl<SPI, GFBD> SortedRenderPhasePlugin<SPI, GFBD>
1242
where
1243
SPI: SortedPhaseItem,
1244
GFBD: GetFullBatchData,
1245
{
1246
pub fn new(debug_flags: RenderDebugFlags) -> Self {
1247
Self {
1248
debug_flags,
1249
phantom: PhantomData,
1250
}
1251
}
1252
}
1253
1254
impl<SPI, GFBD> Plugin for SortedRenderPhasePlugin<SPI, GFBD>
1255
where
1256
SPI: SortedPhaseItem + CachedRenderPipelinePhaseItem,
1257
GFBD: GetFullBatchData + Sync + Send + 'static,
1258
{
1259
fn build(&self, app: &mut App) {
1260
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
1261
return;
1262
};
1263
1264
render_app
1265
.init_resource::<ViewSortedRenderPhases<SPI>>()
1266
.init_resource::<PhaseBatchedInstanceBuffers<SPI, GFBD::BufferData>>()
1267
.insert_resource(PhaseIndirectParametersBuffers::<SPI>::new(
1268
self.debug_flags
1269
.contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
1270
))
1271
.add_systems(
1272
Render,
1273
(
1274
(
1275
no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1276
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
1277
gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
1278
.run_if(
1279
resource_exists::<
1280
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1281
>,
1282
),
1283
)
1284
.in_set(RenderSystems::PrepareResources),
1285
gpu_preprocessing::collect_buffers_for_phase::<SPI, GFBD>
1286
.run_if(
1287
resource_exists::<
1288
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1289
>,
1290
)
1291
.in_set(RenderSystems::PrepareResourcesCollectPhaseBuffers),
1292
),
1293
);
1294
}
1295
}
1296
1297
impl UnbatchableBinnedEntityIndexSet {
1298
/// Adds a new entity to the list of unbatchable binned entities.
1299
pub fn add(&mut self, indices: UnbatchableBinnedEntityIndices) {
1300
match self {
1301
UnbatchableBinnedEntityIndexSet::NoEntities => {
1302
match indices.extra_index {
1303
PhaseItemExtraIndex::DynamicOffset(_) => {
1304
// This is the first entity we've seen, and we don't have
1305
// compute shaders. Initialize an array.
1306
*self = UnbatchableBinnedEntityIndexSet::Dense(vec![indices]);
1307
}
1308
PhaseItemExtraIndex::None => {
1309
// This is the first entity we've seen, and we have compute
1310
// shaders. Initialize the fast path.
1311
*self = UnbatchableBinnedEntityIndexSet::Sparse {
1312
instance_range: indices.instance_index..indices.instance_index + 1,
1313
first_indirect_parameters_index: None,
1314
}
1315
}
1316
PhaseItemExtraIndex::IndirectParametersIndex {
1317
range: ref indirect_parameters_index,
1318
..
1319
} => {
1320
// This is the first entity we've seen, and we have compute
1321
// shaders. Initialize the fast path.
1322
*self = UnbatchableBinnedEntityIndexSet::Sparse {
1323
instance_range: indices.instance_index..indices.instance_index + 1,
1324
first_indirect_parameters_index: NonMaxU32::new(
1325
indirect_parameters_index.start,
1326
),
1327
}
1328
}
1329
}
1330
}
1331
1332
UnbatchableBinnedEntityIndexSet::Sparse {
1333
instance_range,
1334
first_indirect_parameters_index,
1335
} if instance_range.end == indices.instance_index
1336
&& ((first_indirect_parameters_index.is_none()
1337
&& indices.extra_index == PhaseItemExtraIndex::None)
1338
|| first_indirect_parameters_index.is_some_and(
1339
|first_indirect_parameters_index| match indices.extra_index {
1340
PhaseItemExtraIndex::IndirectParametersIndex {
1341
range: ref this_range,
1342
..
1343
} => {
1344
u32::from(first_indirect_parameters_index) + instance_range.end
1345
- instance_range.start
1346
== this_range.start
1347
}
1348
PhaseItemExtraIndex::DynamicOffset(_) | PhaseItemExtraIndex::None => {
1349
false
1350
}
1351
},
1352
)) =>
1353
{
1354
// This is the normal case on non-WebGL 2.
1355
instance_range.end += 1;
1356
}
1357
1358
UnbatchableBinnedEntityIndexSet::Sparse { instance_range, .. } => {
1359
// We thought we were in non-WebGL 2 mode, but we got a dynamic
1360
// offset or non-contiguous index anyway. This shouldn't happen,
1361
// but let's go ahead and do the sensible thing anyhow: demote
1362
// the compressed `NoDynamicOffsets` field to the full
1363
// `DynamicOffsets` array.
1364
warn!(
1365
"Unbatchable binned entity index set was demoted from sparse to dense. \
1366
This is a bug in the renderer. Please report it.",
1367
);
1368
let new_dynamic_offsets = (0..instance_range.len() as u32)
1369
.flat_map(|entity_index| self.indices_for_entity_index(entity_index))
1370
.chain(iter::once(indices))
1371
.collect();
1372
*self = UnbatchableBinnedEntityIndexSet::Dense(new_dynamic_offsets);
1373
}
1374
1375
UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => {
1376
dense_indices.push(indices);
1377
}
1378
}
1379
}
1380
1381
/// Clears the unbatchable binned entity index set.
1382
fn clear(&mut self) {
1383
match self {
1384
UnbatchableBinnedEntityIndexSet::Dense(dense_indices) => dense_indices.clear(),
1385
UnbatchableBinnedEntityIndexSet::Sparse { .. } => {
1386
*self = UnbatchableBinnedEntityIndexSet::NoEntities;
1387
}
1388
_ => {}
1389
}
1390
}
1391
}
1392
1393
/// A collection of all items to be rendered that will be encoded to GPU
1394
/// commands for a single render phase for a single view.
1395
///
1396
/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
1397
/// They are used to queue entities for rendering.
1398
/// Multiple phases might be required due to different sorting/batching behaviors
1399
/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
1400
/// the rendered texture of the previous phase (e.g. for screen-space reflections).
1401
/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
1402
/// The render pass might be reused for multiple phases to reduce GPU overhead.
1403
///
1404
/// This flavor of render phase is used only for meshes that need to be sorted
1405
/// back-to-front, such as transparent meshes. For items that don't need strict
1406
/// sorting, [`BinnedRenderPhase`] is preferred, for performance.
1407
pub struct SortedRenderPhase<I>
1408
where
1409
I: SortedPhaseItem,
1410
{
1411
/// The items within this [`SortedRenderPhase`].
1412
pub items: Vec<I>,
1413
}
1414
1415
impl<I> Default for SortedRenderPhase<I>
1416
where
1417
I: SortedPhaseItem,
1418
{
1419
fn default() -> Self {
1420
Self { items: Vec::new() }
1421
}
1422
}
1423
1424
impl<I> SortedRenderPhase<I>
1425
where
1426
I: SortedPhaseItem,
1427
{
1428
/// Adds a [`PhaseItem`] to this render phase.
1429
#[inline]
1430
pub fn add(&mut self, item: I) {
1431
self.items.push(item);
1432
}
1433
1434
/// Removes all [`PhaseItem`]s from this render phase.
1435
#[inline]
1436
pub fn clear(&mut self) {
1437
self.items.clear();
1438
}
1439
1440
/// Sorts all of its [`PhaseItem`]s.
1441
pub fn sort(&mut self) {
1442
I::sort(&mut self.items);
1443
}
1444
1445
/// An [`Iterator`] through the associated [`Entity`] for each [`PhaseItem`] in order.
1446
#[inline]
1447
pub fn iter_entities(&'_ self) -> impl Iterator<Item = Entity> + '_ {
1448
self.items.iter().map(PhaseItem::entity)
1449
}
1450
1451
/// Renders all of its [`PhaseItem`]s using their corresponding draw functions.
1452
pub fn render<'w>(
1453
&self,
1454
render_pass: &mut TrackedRenderPass<'w>,
1455
world: &'w World,
1456
view: Entity,
1457
) -> Result<(), DrawError> {
1458
self.render_range(render_pass, world, view, ..)
1459
}
1460
1461
/// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
1462
pub fn render_range<'w>(
1463
&self,
1464
render_pass: &mut TrackedRenderPass<'w>,
1465
world: &'w World,
1466
view: Entity,
1467
range: impl SliceIndex<[I], Output = [I]>,
1468
) -> Result<(), DrawError> {
1469
let items = self
1470
.items
1471
.get(range)
1472
.expect("`Range` provided to `render_range()` is out of bounds");
1473
1474
let draw_functions = world.resource::<DrawFunctions<I>>();
1475
let mut draw_functions = draw_functions.write();
1476
draw_functions.prepare(world);
1477
1478
let mut index = 0;
1479
while index < items.len() {
1480
let item = &items[index];
1481
let batch_range = item.batch_range();
1482
if batch_range.is_empty() {
1483
index += 1;
1484
} else {
1485
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
1486
draw_function.draw(world, render_pass, view, item)?;
1487
index += batch_range.len();
1488
}
1489
}
1490
Ok(())
1491
}
1492
}
1493
1494
/// An item (entity of the render world) which will be drawn to a texture or the screen,
1495
/// as part of a render phase.
1496
///
1497
/// The data required for rendering an entity is extracted from the main world in the
1498
/// [`ExtractSchedule`](crate::ExtractSchedule).
1499
/// Then it has to be queued up for rendering during the [`RenderSystems::Queue`],
1500
/// by adding a corresponding phase item to a render phase.
1501
/// Afterwards it will be possibly sorted and rendered automatically in the
1502
/// [`RenderSystems::PhaseSort`] and [`RenderSystems::Render`], respectively.
1503
///
1504
/// `PhaseItem`s come in two flavors: [`BinnedPhaseItem`]s and
1505
/// [`SortedPhaseItem`]s.
1506
///
1507
/// * Binned phase items have a `BinKey` which specifies what bin they're to be
1508
/// placed in. All items in the same bin are eligible to be batched together.
1509
/// The `BinKey`s are sorted, but the individual bin items aren't. Binned phase
1510
/// items are good for opaque meshes, in which the order of rendering isn't
1511
/// important. Generally, binned phase items are faster than sorted phase items.
1512
///
1513
/// * Sorted phase items, on the other hand, are placed into one large buffer
1514
/// and then sorted all at once. This is needed for transparent meshes, which
1515
/// have to be sorted back-to-front to render with the painter's algorithm.
1516
/// These types of phase items are generally slower than binned phase items.
1517
pub trait PhaseItem: Sized + Send + Sync + 'static {
1518
/// Whether or not this `PhaseItem` should be subjected to automatic batching. (Default: `true`)
1519
const AUTOMATIC_BATCHING: bool = true;
1520
1521
/// The corresponding entity that will be drawn.
1522
///
1523
/// This is used to fetch the render data of the entity, required by the draw function,
1524
/// from the render world .
1525
fn entity(&self) -> Entity;
1526
1527
/// The main world entity represented by this `PhaseItem`.
1528
fn main_entity(&self) -> MainEntity;
1529
1530
/// Specifies the [`Draw`] function used to render the item.
1531
fn draw_function(&self) -> DrawFunctionId;
1532
1533
/// The range of instances that the batch covers. After doing a batched draw, batch range
1534
/// length phase items will be skipped. This design is to avoid having to restructure the
1535
/// render phase unnecessarily.
1536
fn batch_range(&self) -> &Range<u32>;
1537
fn batch_range_mut(&mut self) -> &mut Range<u32>;
1538
1539
/// Returns the [`PhaseItemExtraIndex`].
1540
///
1541
/// If present, this is either a dynamic offset or an indirect parameters
1542
/// index.
1543
fn extra_index(&self) -> PhaseItemExtraIndex;
1544
1545
/// Returns a pair of mutable references to both the batch range and extra
1546
/// index.
1547
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex);
1548
}
1549
1550
/// The "extra index" associated with some [`PhaseItem`]s, alongside the
1551
/// indirect instance index.
1552
///
1553
/// Sometimes phase items require another index in addition to the range of
1554
/// instances they already have. These can be:
1555
///
1556
/// * The *dynamic offset*: a `wgpu` dynamic offset into the uniform buffer of
1557
/// instance data. This is used on platforms that don't support storage
1558
/// buffers, to work around uniform buffer size limitations.
1559
///
1560
/// * The *indirect parameters index*: an index into the buffer that specifies
1561
/// the indirect parameters for this [`PhaseItem`]'s drawcall. This is used when
1562
/// indirect mode is on (as used for GPU culling).
1563
///
1564
/// Note that our indirect draw functionality requires storage buffers, so it's
1565
/// impossible to have both a dynamic offset and an indirect parameters index.
1566
/// This convenient fact allows us to pack both indices into a single `u32`.
1567
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
1568
pub enum PhaseItemExtraIndex {
1569
/// No extra index is present.
1570
None,
1571
/// A `wgpu` dynamic offset into the uniform buffer of instance data. This
1572
/// is used on platforms that don't support storage buffers, to work around
1573
/// uniform buffer size limitations.
1574
DynamicOffset(u32),
1575
/// An index into the buffer that specifies the indirect parameters for this
1576
/// [`PhaseItem`]'s drawcall. This is used when indirect mode is on (as used
1577
/// for GPU culling).
1578
IndirectParametersIndex {
1579
/// The range of indirect parameters within the indirect parameters array.
1580
///
1581
/// If we're using `multi_draw_indirect_count`, this specifies the
1582
/// maximum range of indirect parameters within that array. If batches
1583
/// are ultimately culled out on the GPU, the actual number of draw
1584
/// commands might be lower than the length of this range.
1585
range: Range<u32>,
1586
/// If `multi_draw_indirect_count` is in use, and this phase item is
1587
/// part of a batch set, specifies the index of the batch set that this
1588
/// phase item is a part of.
1589
///
1590
/// If `multi_draw_indirect_count` isn't in use, or this phase item
1591
/// isn't part of a batch set, this is `None`.
1592
batch_set_index: Option<NonMaxU32>,
1593
},
1594
}
1595
1596
impl PhaseItemExtraIndex {
1597
/// Returns either an indirect parameters index or
1598
/// [`PhaseItemExtraIndex::None`], as appropriate.
1599
pub fn maybe_indirect_parameters_index(
1600
indirect_parameters_index: Option<NonMaxU32>,
1601
) -> PhaseItemExtraIndex {
1602
match indirect_parameters_index {
1603
Some(indirect_parameters_index) => PhaseItemExtraIndex::IndirectParametersIndex {
1604
range: u32::from(indirect_parameters_index)
1605
..(u32::from(indirect_parameters_index) + 1),
1606
batch_set_index: None,
1607
},
1608
None => PhaseItemExtraIndex::None,
1609
}
1610
}
1611
1612
/// Returns either a dynamic offset index or [`PhaseItemExtraIndex::None`],
1613
/// as appropriate.
1614
pub fn maybe_dynamic_offset(dynamic_offset: Option<NonMaxU32>) -> PhaseItemExtraIndex {
1615
match dynamic_offset {
1616
Some(dynamic_offset) => PhaseItemExtraIndex::DynamicOffset(dynamic_offset.into()),
1617
None => PhaseItemExtraIndex::None,
1618
}
1619
}
1620
}
1621
1622
/// Represents phase items that are placed into bins. The `BinKey` specifies
1623
/// which bin they're to be placed in. Bin keys are sorted, and items within the
1624
/// same bin are eligible to be batched together. The elements within the bins
1625
/// aren't themselves sorted.
1626
///
1627
/// An example of a binned phase item is `Opaque3d`, for which the rendering
1628
/// order isn't critical.
1629
pub trait BinnedPhaseItem: PhaseItem {
1630
/// The key used for binning [`PhaseItem`]s into bins. Order the members of
1631
/// [`BinnedPhaseItem::BinKey`] by the order of binding for best
1632
/// performance. For example, pipeline id, draw function id, mesh asset id,
1633
/// lowest variable bind group id such as the material bind group id, and
1634
/// its dynamic offsets if any, next bind group and offsets, etc. This
1635
/// reduces the need for rebinding between bins and improves performance.
1636
type BinKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash;
1637
1638
/// The key used to combine batches into batch sets.
1639
///
1640
/// A *batch set* is a set of meshes that can potentially be multi-drawn
1641
/// together.
1642
type BatchSetKey: PhaseItemBatchSetKey;
1643
1644
/// Creates a new binned phase item from the key and per-entity data.
1645
///
1646
/// Unlike [`SortedPhaseItem`]s, this is generally called "just in time"
1647
/// before rendering. The resulting phase item isn't stored in any data
1648
/// structures, resulting in significant memory savings.
1649
fn new(
1650
batch_set_key: Self::BatchSetKey,
1651
bin_key: Self::BinKey,
1652
representative_entity: (Entity, MainEntity),
1653
batch_range: Range<u32>,
1654
extra_index: PhaseItemExtraIndex,
1655
) -> Self;
1656
}
1657
1658
/// A key used to combine batches into batch sets.
1659
///
1660
/// A *batch set* is a set of meshes that can potentially be multi-drawn
1661
/// together.
1662
pub trait PhaseItemBatchSetKey: Clone + Send + Sync + PartialEq + Eq + Ord + Hash {
1663
/// Returns true if this batch set key describes indexed meshes or false if
1664
/// it describes non-indexed meshes.
1665
///
1666
/// Bevy uses this in order to determine which kind of indirect draw
1667
/// parameters to use, if indirect drawing is enabled.
1668
fn indexed(&self) -> bool;
1669
}
1670
1671
/// Represents phase items that must be sorted. The `SortKey` specifies the
1672
/// order that these items are drawn in. These are placed into a single array,
1673
/// and the array as a whole is then sorted.
1674
///
1675
/// An example of a sorted phase item is `Transparent3d`, which must be sorted
1676
/// back to front in order to correctly render with the painter's algorithm.
1677
pub trait SortedPhaseItem: PhaseItem {
1678
/// The type used for ordering the items. The smallest values are drawn first.
1679
/// This order can be calculated using the [`ViewRangefinder3d`],
1680
/// based on the view-space `Z` value of the corresponding view matrix.
1681
type SortKey: Ord;
1682
1683
/// Determines the order in which the items are drawn.
1684
fn sort_key(&self) -> Self::SortKey;
1685
1686
/// Sorts a slice of phase items into render order. Generally if the same type
1687
/// is batched this should use a stable sort like [`slice::sort_by_key`].
1688
/// In almost all other cases, this should not be altered from the default,
1689
/// which uses an unstable sort, as this provides the best balance of CPU and GPU
1690
/// performance.
1691
///
1692
/// Implementers can optionally not sort the list at all. This is generally advisable if and
1693
/// only if the renderer supports a depth prepass, which is by default not supported by
1694
/// the rest of Bevy's first party rendering crates. Even then, this may have a negative
1695
/// impact on GPU-side performance due to overdraw.
1696
///
1697
/// It's advised to always profile for performance changes when changing this implementation.
1698
#[inline]
1699
fn sort(items: &mut [Self]) {
1700
items.sort_unstable_by_key(Self::sort_key);
1701
}
1702
1703
/// Whether this phase item targets indexed meshes (those with both vertex
1704
/// and index buffers as opposed to just vertex buffers).
1705
///
1706
/// Bevy needs this information in order to properly group phase items
1707
/// together for multi-draw indirect, because the GPU layout of indirect
1708
/// commands differs between indexed and non-indexed meshes.
1709
///
1710
/// If you're implementing a custom phase item that doesn't describe a mesh,
1711
/// you can safely return false here.
1712
fn indexed(&self) -> bool;
1713
}
1714
1715
/// A [`PhaseItem`] item, that automatically sets the appropriate render pipeline,
1716
/// cached in the [`PipelineCache`].
1717
///
1718
/// You can use the [`SetItemPipeline`] render command to set the pipeline for this item.
1719
pub trait CachedRenderPipelinePhaseItem: PhaseItem {
1720
/// The id of the render pipeline, cached in the [`PipelineCache`], that will be used to draw
1721
/// this phase item.
1722
fn cached_pipeline(&self) -> CachedRenderPipelineId;
1723
}
1724
1725
/// A [`RenderCommand`] that sets the pipeline for the [`CachedRenderPipelinePhaseItem`].
1726
pub struct SetItemPipeline;
1727
1728
impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
1729
type Param = SRes<PipelineCache>;
1730
type ViewQuery = ();
1731
type ItemQuery = ();
1732
#[inline]
1733
fn render<'w>(
1734
item: &P,
1735
_view: (),
1736
_entity: Option<()>,
1737
pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
1738
pass: &mut TrackedRenderPass<'w>,
1739
) -> RenderCommandResult {
1740
if let Some(pipeline) = pipeline_cache
1741
.into_inner()
1742
.get_render_pipeline(item.cached_pipeline())
1743
{
1744
pass.set_render_pipeline(pipeline);
1745
RenderCommandResult::Success
1746
} else {
1747
RenderCommandResult::Skip
1748
}
1749
}
1750
}
1751
1752
/// This system sorts the [`PhaseItem`]s of all [`SortedRenderPhase`]s of this
1753
/// type.
1754
pub fn sort_phase_system<I>(mut render_phases: ResMut<ViewSortedRenderPhases<I>>)
1755
where
1756
I: SortedPhaseItem,
1757
{
1758
for phase in render_phases.values_mut() {
1759
phase.sort();
1760
}
1761
}
1762
1763
/// Removes entities that became invisible or changed phases from the bins.
1764
///
1765
/// This must run after queuing.
1766
pub fn sweep_old_entities<BPI>(mut render_phases: ResMut<ViewBinnedRenderPhases<BPI>>)
1767
where
1768
BPI: BinnedPhaseItem,
1769
{
1770
for phase in render_phases.0.values_mut() {
1771
phase.sweep_old_entities();
1772
}
1773
}
1774
1775
impl BinnedRenderPhaseType {
1776
pub fn mesh(
1777
batchable: bool,
1778
gpu_preprocessing_support: &GpuPreprocessingSupport,
1779
) -> BinnedRenderPhaseType {
1780
match (batchable, gpu_preprocessing_support.max_supported_mode) {
1781
(true, GpuPreprocessingMode::Culling) => BinnedRenderPhaseType::MultidrawableMesh,
1782
(true, _) => BinnedRenderPhaseType::BatchableMesh,
1783
(false, _) => BinnedRenderPhaseType::UnbatchableMesh,
1784
}
1785
}
1786
}
1787
1788
impl RenderBin {
1789
/// Creates a [`RenderBin`] containing a single entity.
1790
fn from_entity(entity: MainEntity, uniform_index: InputUniformIndex) -> RenderBin {
1791
let mut entities = IndexMap::default();
1792
entities.insert(entity, uniform_index);
1793
RenderBin { entities }
1794
}
1795
1796
/// Inserts an entity into the bin.
1797
fn insert(&mut self, entity: MainEntity, uniform_index: InputUniformIndex) {
1798
self.entities.insert(entity, uniform_index);
1799
}
1800
1801
/// Removes an entity from the bin.
1802
fn remove(&mut self, entity_to_remove: MainEntity) {
1803
self.entities.swap_remove(&entity_to_remove);
1804
}
1805
1806
/// Returns true if the bin contains no entities.
1807
fn is_empty(&self) -> bool {
1808
self.entities.is_empty()
1809
}
1810
1811
/// Returns the [`IndexMap`] containing all the entities in the bin, along
1812
/// with the cached [`InputUniformIndex`] of each.
1813
#[inline]
1814
pub fn entities(&self) -> &IndexMap<MainEntity, InputUniformIndex, EntityHash> {
1815
&self.entities
1816
}
1817
}
1818
1819
/// An iterator that efficiently finds the indices of all zero bits in a
1820
/// [`FixedBitSet`] and returns them in reverse order.
1821
///
1822
/// [`FixedBitSet`] doesn't natively offer this functionality, so we have to
1823
/// implement it ourselves.
1824
#[derive(Debug)]
1825
struct ReverseFixedBitSetZeroesIterator<'a> {
1826
/// The bit set.
1827
bitset: &'a FixedBitSet,
1828
/// The next bit index we're going to scan when [`Iterator::next`] is
1829
/// called.
1830
bit_index: isize,
1831
}
1832
1833
impl<'a> ReverseFixedBitSetZeroesIterator<'a> {
1834
fn new(bitset: &'a FixedBitSet) -> ReverseFixedBitSetZeroesIterator<'a> {
1835
ReverseFixedBitSetZeroesIterator {
1836
bitset,
1837
bit_index: (bitset.len() as isize) - 1,
1838
}
1839
}
1840
}
1841
1842
impl<'a> Iterator for ReverseFixedBitSetZeroesIterator<'a> {
1843
type Item = usize;
1844
1845
fn next(&mut self) -> Option<usize> {
1846
while self.bit_index >= 0 {
1847
// Unpack the bit index into block and bit.
1848
let block_index = self.bit_index / (Block::BITS as isize);
1849
let bit_pos = self.bit_index % (Block::BITS as isize);
1850
1851
// Grab the block. Mask off all bits above the one we're scanning
1852
// from by setting them all to 1.
1853
let mut block = self.bitset.as_slice()[block_index as usize];
1854
if bit_pos + 1 < (Block::BITS as isize) {
1855
block |= (!0) << (bit_pos + 1);
1856
}
1857
1858
// Search for the next unset bit. Note that the `leading_ones`
1859
// function counts from the MSB to the LSB, so we need to flip it to
1860
// get the bit number.
1861
let pos = (Block::BITS as isize) - (block.leading_ones() as isize) - 1;
1862
1863
// If we found an unset bit, return it.
1864
if pos != -1 {
1865
let result = block_index * (Block::BITS as isize) + pos;
1866
self.bit_index = result - 1;
1867
return Some(result as usize);
1868
}
1869
1870
// Otherwise, go to the previous block.
1871
self.bit_index = block_index * (Block::BITS as isize) - 1;
1872
}
1873
1874
None
1875
}
1876
}
1877
1878
#[cfg(test)]
1879
mod test {
1880
use super::ReverseFixedBitSetZeroesIterator;
1881
use fixedbitset::FixedBitSet;
1882
use proptest::{collection::vec, prop_assert_eq, proptest};
1883
1884
proptest! {
1885
#[test]
1886
fn reverse_fixed_bit_set_zeroes_iterator(
1887
bits in vec(0usize..1024usize, 0usize..1024usize),
1888
size in 0usize..1024usize,
1889
) {
1890
// Build a random bit set.
1891
let mut bitset = FixedBitSet::new();
1892
bitset.grow(size);
1893
for bit in bits {
1894
if bit < size {
1895
bitset.set(bit, true);
1896
}
1897
}
1898
1899
// Iterate over the bit set backwards in a naive way, and check that
1900
// that iteration sequence corresponds to the optimized one.
1901
let mut iter = ReverseFixedBitSetZeroesIterator::new(&bitset);
1902
for bit_index in (0..size).rev() {
1903
if !bitset.contains(bit_index) {
1904
prop_assert_eq!(iter.next(), Some(bit_index));
1905
}
1906
}
1907
1908
prop_assert_eq!(iter.next(), None);
1909
}
1910
}
1911
}
1912
1913