Skip to content

Commit ef9ff96

Browse files
Charles BournhonesquecBournhonesque
authored andcommitted
store StorageId as Cow<[StorageId]>
Change-Id: I0953e9836f8ddfd8923c4b00132d9e653c575572
1 parent 7fceef0 commit ef9ff96

File tree

3 files changed

+43
-29
lines changed

3 files changed

+43
-29
lines changed

crates/bevy_ecs/src/query/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ impl QueryCache for Uncached {
463463
/// We do not update anything. This is here for feature parity.
464464
fn update_archetypes<D: QueryData, F: QueryFilter>(
465465
&mut self,
466-
_: &QueryState<D, F, Uncached>,
466+
_: &QueryPrefix<D, F>,
467467
_: UnsafeWorldCell,
468468
) {
469469
}

crates/bevy_ecs/src/query/iter.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> Iterator for QueryIter
945945
accum = func(accum, item);
946946
}
947947

948-
for id in self.cursor.storage_id_iter.clone().copied() {
948+
let mut i = self.cursor.storage_index;
949+
let len = self.cursor.storage_ids.as_ref().len();
950+
while i < len {
951+
// Take a short-lived copy of the id to avoid holding a borrow of `self` across the call
952+
let id = self.cursor.storage_ids.as_ref()[i];
953+
i += 1;
949954
// SAFETY:
950955
// - The range(None) is equivalent to [0, storage.entity_count)
951956
accum = unsafe { self.fold_over_storage_range(accum, &mut func, id, None) };
@@ -2449,8 +2454,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache, const K: usize> Debug
24492454
struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache> {
24502455
// whether the query iteration is dense or not.
24512456
is_dense: bool,
2452-
storage_id_iter: core::slice::Iter<'s, StorageId>,
2453-
owned_storage_ids: Option<Vec<StorageId>>,
2457+
// Storage ids to iterate over; owned for uncached, borrowed for cached
2458+
storage_ids: Cow<'s, [StorageId]>,
2459+
// Current index into storage_ids
2460+
storage_index: usize,
24542461
table_entities: &'w [Entity],
24552462
archetype_entities: &'w [ArchetypeEntity],
24562463
fetch: D::Fetch<'w>,
@@ -2466,8 +2473,8 @@ impl<D: QueryData, F: QueryFilter, C: QueryCache> Clone for QueryIterationCursor
24662473
fn clone(&self) -> Self {
24672474
Self {
24682475
is_dense: self.is_dense,
2469-
storage_id_iter: self.storage_id_iter.clone(),
2470-
owned_storage_ids: self.owned_storage_ids.clone(),
2476+
storage_ids: self.storage_ids.clone(),
2477+
storage_index: self.storage_index,
24712478
table_entities: self.table_entities,
24722479
archetype_entities: self.archetype_entities,
24732480
fetch: self.fetch.clone(),
@@ -2492,7 +2499,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
24922499
this_run: Tick,
24932500
) -> Self {
24942501
QueryIterationCursor {
2495-
storage_id_iter: [].iter(),
2502+
storage_ids: Cow::Borrowed(&[]),
2503+
storage_index: 0,
24962504
..Self::init(world, query_state, last_run, this_run)
24972505
}
24982506
}
@@ -2509,27 +2517,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
25092517
let fetch = D::init_fetch(world, &query_state.fetch_state, last_run, this_run);
25102518
let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run);
25112519
let iteration_data = query_state.cache.iteration_data(query_state, world);
2512-
let (owned_storage_ids, storage_id_iter) = match iteration_data.storage_ids {
2513-
Cow::Borrowed(slice) => (None, slice.iter()),
2514-
Cow::Owned(owned) => {
2515-
let owned_ids = Some(owned);
2516-
let slice_ptr: *const [StorageId] = owned_ids.as_ref().unwrap().as_slice();
2517-
// SAFETY:
2518-
// - The slice pointer refers to memory owned by `owned_ids`
2519-
// - `owned_ids` is moved *as a whole* into the struct below and won’t move afterward
2520-
// - The struct owns `owned_ids` for the same lifetime as `ids_iter`
2521-
// => Safe as long as `Cursor` isn’t moved after creation (so don’t mem::swap etc.)
2522-
let ids_iter = unsafe { (&*slice_ptr).iter() };
2523-
(owned_ids, ids_iter)
2524-
}
2525-
};
2520+
let storage_ids = iteration_data.storage_ids;
25262521
QueryIterationCursor {
25272522
fetch,
25282523
filter,
25292524
table_entities: &[],
25302525
archetype_entities: &[],
2531-
storage_id_iter,
2532-
owned_storage_ids,
2526+
storage_ids,
2527+
storage_index: 0,
25332528
is_dense: iteration_data.is_dense,
25342529
current_len: 0,
25352530
current_row: 0,
@@ -2544,8 +2539,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
25442539
filter: F::shrink_fetch(self.filter.clone()),
25452540
table_entities: self.table_entities,
25462541
archetype_entities: self.archetype_entities,
2547-
storage_id_iter: self.storage_id_iter.clone(),
2548-
owned_storage_ids: self.owned_storage_ids.clone(),
2542+
storage_ids: self.storage_ids.clone(),
2543+
storage_index: self.storage_index,
25492544
current_len: self.current_len,
25502545
current_row: self.current_row,
25512546
_cache: core::marker::PhantomData,
@@ -2605,7 +2600,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
26052600
/// Note that if `F::IS_ARCHETYPAL`, the return value
26062601
/// will be **the exact count of remaining values**.
26072602
fn max_remaining(&self, tables: &'w Tables, archetypes: &'w Archetypes) -> u32 {
2608-
let ids = self.storage_id_iter.clone();
2603+
let ids = self.storage_ids.as_ref()[self.storage_index..].iter().copied();
26092604
let remaining_matched: u32 = if self.is_dense {
26102605
// SAFETY: The if check ensures that storage_id_iter stores TableIds
26112606
unsafe { ids.map(|id| tables[id.table_id].entity_count()).sum() }
@@ -2635,7 +2630,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
26352630
loop {
26362631
// we are on the beginning of the query, or finished processing a table, so skip to the next
26372632
if self.current_row == self.current_len {
2638-
let table_id = self.storage_id_iter.next()?.table_id;
2633+
let table_id = {
2634+
let id = *self
2635+
.storage_ids
2636+
.as_ref()
2637+
.get(self.storage_index)?;
2638+
self.storage_index += 1;
2639+
id.table_id
2640+
};
26392641
let table = tables.get(table_id).debug_checked_unwrap();
26402642
if table.is_empty() {
26412643
continue;
@@ -2676,7 +2678,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter, C: QueryCache + 's>
26762678
} else {
26772679
loop {
26782680
if self.current_row == self.current_len {
2679-
let archetype_id = self.storage_id_iter.next()?.archetype_id;
2681+
let archetype_id = {
2682+
let id = *self
2683+
.storage_ids
2684+
.as_ref()
2685+
.get(self.storage_index)?;
2686+
self.storage_index += 1;
2687+
id.archetype_id
2688+
};
26802689
let archetype = archetypes.get(archetype_id).debug_checked_unwrap();
26812690
if archetype.is_empty() {
26822691
continue;

crates/bevy_ecs/src/query/state.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,16 @@ pub(super) union StorageId {
5757
/// [`State`]: crate::query::world_query::WorldQuery::State
5858
/// [`Fetch`]: crate::query::world_query::WorldQuery::Fetch
5959
/// [`Table`]: crate::storage::Table
60+
#[cfg(feature = "query_uncached_default")]
61+
pub type DefaultCache = Uncached;
62+
#[cfg(not(feature = "query_uncached_default"))]
63+
pub type DefaultCache = CacheState;
64+
6065
#[repr(C)]
6166
// SAFETY NOTE:
6267
// Do not add any new fields that use the `D` or `F` generic parameters as this may
6368
// make `QueryState::as_transmuted_state` unsound if not done with care.
64-
pub struct QueryState<D: QueryData, F: QueryFilter = (), C: QueryCache = CacheState> {
69+
pub struct QueryState<D: QueryData, F: QueryFilter = (), C: QueryCache = DefaultCache> {
6570
world_id: WorldId,
6671
/// [`FilteredAccess`] computed by combining the `D` and `F` access. Used to check which other queries
6772
/// this query can run in parallel with.

0 commit comments

Comments
 (0)