Skip to content

Commit 172c020

Browse files
authored
Cache opaque deferred entities so we don't have to continuously re-queue them. (#18007)
Even though opaque deferred entities aren't placed into the `Opaque3d` bin, we still want to cache them as though they were, so that we don't have to re-queue them every frame. This commit implements that logic, reducing the time of `queue_material_meshes` to near-zero on Caldera.
1 parent 5d7a605 commit 172c020

File tree

2 files changed

+87
-29
lines changed

2 files changed

+87
-29
lines changed

crates/bevy_pbr/src/material.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,11 @@ pub fn queue_material_meshes<M: Material>(
10361036
}
10371037
RenderPhaseType::Opaque => {
10381038
if material.properties.render_method == OpaqueRendererMethod::Deferred {
1039+
// Even though we aren't going to insert the entity into
1040+
// a bin, we still want to update its cache entry. That
1041+
// way, we know we don't need to re-examine it in future
1042+
// frames.
1043+
opaque_phase.update_cache(*visible_entity, None, current_change_tick);
10391044
continue;
10401045
}
10411046
let batch_set_key = Opaque3dBatchSetKey {

crates/bevy_render/src/render_phase/mod.rs

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ where
148148
/// We retain these so that, when the entity changes,
149149
/// [`Self::sweep_old_entities`] can quickly find the bin it was located in
150150
/// and remove it.
151-
cached_entity_bin_keys: IndexMap<MainEntity, CachedBinKey<BPI>, EntityHash>,
151+
cached_entity_bin_keys: IndexMap<MainEntity, CachedBinnedEntity<BPI>, EntityHash>,
152152

153153
/// The set of indices in [`Self::cached_entity_bin_keys`] that are
154154
/// confirmed to be up to date.
@@ -185,10 +185,23 @@ where
185185
/// The entity.
186186
main_entity: MainEntity,
187187
/// The key that identifies the bin that this entity used to be in.
188-
old_bin_key: CachedBinKey<BPI>,
188+
old_cached_binned_entity: CachedBinnedEntity<BPI>,
189189
}
190190

191191
/// Information that we keep about an entity currently within a bin.
192+
pub struct CachedBinnedEntity<BPI>
193+
where
194+
BPI: BinnedPhaseItem,
195+
{
196+
/// Information that we use to identify a cached entity in a bin.
197+
pub cached_bin_key: Option<CachedBinKey<BPI>>,
198+
/// The last modified tick of the entity.
199+
///
200+
/// We use this to detect when the entity needs to be invalidated.
201+
pub change_tick: Tick,
202+
}
203+
204+
/// Information that we use to identify a cached entity in a bin.
192205
pub struct CachedBinKey<BPI>
193206
where
194207
BPI: BinnedPhaseItem,
@@ -200,10 +213,18 @@ where
200213
/// The type of render phase that we use to render the entity: multidraw,
201214
/// plain batch, etc.
202215
pub phase_type: BinnedRenderPhaseType,
203-
/// The last modified tick of the entity.
204-
///
205-
/// We use this to detect when the entity needs to be invalidated.
206-
pub change_tick: Tick,
216+
}
217+
218+
impl<BPI> Clone for CachedBinnedEntity<BPI>
219+
where
220+
BPI: BinnedPhaseItem,
221+
{
222+
fn clone(&self) -> Self {
223+
CachedBinnedEntity {
224+
cached_bin_key: self.cached_bin_key.clone(),
225+
change_tick: self.change_tick,
226+
}
227+
}
207228
}
208229

209230
impl<BPI> Clone for CachedBinKey<BPI>
@@ -215,11 +236,21 @@ where
215236
batch_set_key: self.batch_set_key.clone(),
216237
bin_key: self.bin_key.clone(),
217238
phase_type: self.phase_type,
218-
change_tick: self.change_tick,
219239
}
220240
}
221241
}
222242

243+
impl<BPI> PartialEq for CachedBinKey<BPI>
244+
where
245+
BPI: BinnedPhaseItem,
246+
{
247+
fn eq(&self, other: &Self) -> bool {
248+
self.batch_set_key == other.batch_set_key
249+
&& self.bin_key == other.bin_key
250+
&& self.phase_type == other.phase_type
251+
}
252+
}
253+
223254
/// How we store and render the batch sets.
224255
///
225256
/// Each one of these corresponds to a [`GpuPreprocessingMode`].
@@ -504,27 +535,41 @@ where
504535
}
505536
}
506537

507-
let new_bin_key = CachedBinKey {
508-
batch_set_key,
509-
bin_key,
510-
phase_type,
538+
// Update the cache.
539+
self.update_cache(
540+
main_entity,
541+
Some(CachedBinKey {
542+
batch_set_key,
543+
bin_key,
544+
phase_type,
545+
}),
546+
change_tick,
547+
);
548+
}
549+
550+
/// Inserts an entity into the cache with the given change tick.
551+
pub fn update_cache(
552+
&mut self,
553+
main_entity: MainEntity,
554+
cached_bin_key: Option<CachedBinKey<BPI>>,
555+
change_tick: Tick,
556+
) {
557+
let new_cached_binned_entity = CachedBinnedEntity {
558+
cached_bin_key,
511559
change_tick,
512560
};
513561

514-
let (index, old_bin_key) = self
562+
let (index, old_cached_binned_entity) = self
515563
.cached_entity_bin_keys
516-
.insert_full(main_entity, new_bin_key.clone());
564+
.insert_full(main_entity, new_cached_binned_entity.clone());
517565

518566
// If the entity changed bins, record its old bin so that we can remove
519567
// the entity from it.
520-
if let Some(old_bin_key) = old_bin_key {
521-
if old_bin_key.batch_set_key != new_bin_key.batch_set_key
522-
|| old_bin_key.bin_key != new_bin_key.bin_key
523-
|| old_bin_key.phase_type != new_bin_key.phase_type
524-
{
568+
if let Some(old_cached_binned_entity) = old_cached_binned_entity {
569+
if old_cached_binned_entity.cached_bin_key != new_cached_binned_entity.cached_bin_key {
525570
self.entities_that_changed_bins.push(EntityThatChangedBins {
526571
main_entity,
527-
old_bin_key,
572+
old_cached_binned_entity,
528573
});
529574
}
530575
}
@@ -826,27 +871,35 @@ where
826871
// reverse order because `swap_remove_index` will potentially invalidate
827872
// all indices after the one we remove.
828873
for index in ReverseFixedBitSetZeroesIterator::new(&self.valid_cached_entity_bin_keys) {
829-
let Some((entity, entity_bin_key)) =
874+
let Some((entity, cached_binned_entity)) =
830875
self.cached_entity_bin_keys.swap_remove_index(index)
831876
else {
832877
continue;
833878
};
834879

835-
remove_entity_from_bin(
836-
entity,
837-
&entity_bin_key,
838-
&mut self.multidrawable_meshes,
839-
&mut self.batchable_meshes,
840-
&mut self.unbatchable_meshes,
841-
&mut self.non_mesh_items,
842-
);
880+
if let Some(ref cached_bin_key) = cached_binned_entity.cached_bin_key {
881+
remove_entity_from_bin(
882+
entity,
883+
cached_bin_key,
884+
&mut self.multidrawable_meshes,
885+
&mut self.batchable_meshes,
886+
&mut self.unbatchable_meshes,
887+
&mut self.non_mesh_items,
888+
);
889+
}
843890
}
844891

845892
// If an entity changed bins, we need to remove it from its old bin.
846893
for entity_that_changed_bins in self.entities_that_changed_bins.drain(..) {
894+
let Some(ref old_cached_bin_key) = entity_that_changed_bins
895+
.old_cached_binned_entity
896+
.cached_bin_key
897+
else {
898+
continue;
899+
};
847900
remove_entity_from_bin(
848901
entity_that_changed_bins.main_entity,
849-
&entity_that_changed_bins.old_bin_key,
902+
old_cached_bin_key,
850903
&mut self.multidrawable_meshes,
851904
&mut self.batchable_meshes,
852905
&mut self.unbatchable_meshes,

0 commit comments

Comments
 (0)