Skip to content

Commit 389df18

Browse files
committed
Change check_visibility to use thread-local queues instead of a channel (#4663)
# Objective Further speed up visibility checking by removing the main sources of contention for the system. ## Solution - ~~Make `ComputedVisibility` a resource wrapping a `FixedBitset`.~~ - ~~Remove `ComputedVisibility` as a component.~~ ~~This adds a one-bit overhead to every entity in the app world. For a game with 100,000 entities, this is 12.5KB of memory. This is still small enough to fit entirely in most L1 caches. Also removes the need for a per-Entity change detection tick. This reduces the memory footprint of ComputedVisibility 72x.~~ ~~The decreased memory usage and less fragmented memory locality should provide significant performance benefits.~~ ~~Clearing visible entities should be significantly faster than before:~~ - ~~Setting one `u32` to 0 clears 32 entities per cycle.~~ - ~~No archetype fragmentation to contend with.~~ - ~~Change detection is applied to the resource, so there is no per-Entity update tick requirement.~~ ~~The side benefit of this design is that it removes one more "computed component" from userspace. Though accessing the values within it are now less ergonomic.~~ This PR changes `crossbeam_channel` in `check_visibility` to use a `Local<ThreadLocal<Cell<Vec<Entity>>>` to mark down visible entities instead. Co-Authored-By: TheRawMeatball <[email protected]> Co-Authored-By: Aevyrie <[email protected]>
1 parent 511bcc9 commit 389df18

File tree

2 files changed

+31
-17
lines changed

2 files changed

+31
-17
lines changed

crates/bevy_render/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ bitflags = "1.2.1"
5454
smallvec = { version = "1.6", features = ["union", "const_generics"] }
5555
once_cell = "1.4.1" # TODO: replace once_cell with std equivalent if/when this lands: https://github.com/rust-lang/rfcs/pull/2788
5656
downcast-rs = "1.2.0"
57+
thread_local = "1.1"
5758
thiserror = "1.0"
5859
futures-lite = "1.4.0"
59-
crossbeam-channel = "0.5.0"
6060
anyhow = "1.0"
6161
hex = "0.4.2"
6262
hexasphere = "7.0.0"

crates/bevy_render/src/view/visibility/mod.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use bevy_reflect::std_traits::ReflectDefault;
1010
use bevy_reflect::Reflect;
1111
use bevy_transform::components::GlobalTransform;
1212
use bevy_transform::TransformSystem;
13+
use std::cell::Cell;
14+
use thread_local::ThreadLocal;
1315

1416
use crate::{
1517
camera::{Camera, CameraProjection, OrthographicProjection, PerspectiveProjection, Projection},
@@ -148,22 +150,30 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
148150
}
149151

150152
pub fn check_visibility(
153+
mut thread_queues: Local<ThreadLocal<Cell<Vec<Entity>>>>,
151154
mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
152-
mut visible_entity_query: Query<(
153-
Entity,
154-
&Visibility,
155-
&mut ComputedVisibility,
156-
Option<&RenderLayers>,
157-
Option<&Aabb>,
158-
Option<&NoFrustumCulling>,
159-
Option<&GlobalTransform>,
155+
mut visible_entity_query: ParamSet<(
156+
Query<&mut ComputedVisibility>,
157+
Query<(
158+
Entity,
159+
&Visibility,
160+
&mut ComputedVisibility,
161+
Option<&RenderLayers>,
162+
Option<&Aabb>,
163+
Option<&NoFrustumCulling>,
164+
Option<&GlobalTransform>,
165+
)>,
160166
)>,
161167
) {
168+
// Reset the computed visibility to false
169+
for mut computed_visibility in visible_entity_query.p0().iter_mut() {
170+
computed_visibility.is_visible = false;
171+
}
172+
162173
for (mut visible_entities, frustum, maybe_view_mask) in view_query.iter_mut() {
163174
let view_mask = maybe_view_mask.copied().unwrap_or_default();
164-
let (visible_entity_sender, visible_entity_receiver) = crossbeam_channel::unbounded();
165-
166-
visible_entity_query.par_for_each_mut(
175+
visible_entities.entities.clear();
176+
visible_entity_query.p1().par_for_each_mut(
167177
1024,
168178
|(
169179
entity,
@@ -174,12 +184,10 @@ pub fn check_visibility(
174184
maybe_no_frustum_culling,
175185
maybe_transform,
176186
)| {
177-
// Reset visibility
178-
computed_visibility.is_visible = false;
179-
180187
if !visibility.is_visible {
181188
return;
182189
}
190+
183191
let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
184192
if !view_mask.intersects(&entity_mask) {
185193
return;
@@ -205,9 +213,15 @@ pub fn check_visibility(
205213
}
206214

207215
computed_visibility.is_visible = true;
208-
visible_entity_sender.send(entity).ok();
216+
let cell = thread_queues.get_or_default();
217+
let mut queue = cell.take();
218+
queue.push(entity);
219+
cell.set(queue);
209220
},
210221
);
211-
visible_entities.entities = visible_entity_receiver.try_iter().collect();
222+
223+
for cell in thread_queues.iter_mut() {
224+
visible_entities.entities.append(cell.get_mut());
225+
}
212226
}
213227
}

0 commit comments

Comments
 (0)