Skip to content

Commit b700cf6

Browse files
committed
adjust table/archetype entity_count/len calls and indices into tables
1 parent d661430 commit b700cf6

File tree

7 files changed

+154
-96
lines changed

7 files changed

+154
-96
lines changed

crates/bevy_ecs/src/archetype.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use alloc::{boxed::Box, vec::Vec};
3232
use bevy_platform::collections::{hash_map::Entry, HashMap};
3333
use core::{
3434
hash::Hash,
35-
ops::{Index, IndexMut, RangeFrom},
35+
ops::{Index, IndexMut, Range, RangeFrom},
3636
};
3737
use nonmax::NonMaxU32;
3838

@@ -394,6 +394,7 @@ pub struct Archetype {
394394
table_id: TableId,
395395
edges: Edges,
396396
entities: Vec<ArchetypeEntity>,
397+
disabled_entities: u32,
397398
components: ImmutableSparseSet<ComponentId, ArchetypeComponentInfo>,
398399
pub(crate) flags: ArchetypeFlags,
399400
}
@@ -453,6 +454,7 @@ impl Archetype {
453454
id,
454455
table_id,
455456
entities: Vec::new(),
457+
disabled_entities: 0,
456458
components: archetype_components.into_immutable(),
457459
edges: Default::default(),
458460
flags,
@@ -485,6 +487,15 @@ impl Archetype {
485487
&self.entities
486488
}
487489

490+
/// Get the valid table rows (i.e. non-disabled entities).
491+
#[inline]
492+
pub fn archetype_rows(&self) -> Range<u32> {
493+
// - No entity row may be in more than one table row at once, so there are no duplicates,
494+
// and there can not be an entity row of u32::MAX. Therefore, this can not be max either.
495+
// - self.disabled_entities <= self.len()
496+
self.disabled_entities..self.len()
497+
}
498+
488499
/// Fetches the entities contained in this archetype.
489500
#[inline]
490501
pub fn entities_with_location(&self) -> impl Iterator<Item = (Entity, EntityLocation)> {
@@ -650,6 +661,12 @@ impl Archetype {
650661
self.entities.len() as u32
651662
}
652663

664+
/// Gets the number of entities that belong to the archetype, without disabled entities.
665+
#[inline]
666+
pub fn entity_count(&self) -> u32 {
667+
self.len() - self.disabled_entities
668+
}
669+
653670
/// Checks if the archetype has any entities.
654671
#[inline]
655672
pub fn is_empty(&self) -> bool {

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,15 +1725,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
17251725
) {
17261726
let column = table.get_column(component_id).debug_checked_unwrap();
17271727
let table_data = Some((
1728-
column.get_data_slice(table.entity_count() as usize).into(),
1728+
column.get_data_slice(table.len() as usize).into(),
1729+
column.get_added_ticks_slice(table.len() as usize).into(),
1730+
column.get_changed_ticks_slice(table.len() as usize).into(),
17291731
column
1730-
.get_added_ticks_slice(table.entity_count() as usize)
1731-
.into(),
1732-
column
1733-
.get_changed_ticks_slice(table.entity_count() as usize)
1734-
.into(),
1735-
column
1736-
.get_changed_by_slice(table.entity_count() as usize)
1732+
.get_changed_by_slice(table.len() as usize)
17371733
.map(Into::into),
17381734
));
17391735
// SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table
@@ -1931,15 +1927,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
19311927
) {
19321928
let column = table.get_column(component_id).debug_checked_unwrap();
19331929
let table_data = Some((
1934-
column.get_data_slice(table.entity_count() as usize).into(),
1935-
column
1936-
.get_added_ticks_slice(table.entity_count() as usize)
1937-
.into(),
1938-
column
1939-
.get_changed_ticks_slice(table.entity_count() as usize)
1940-
.into(),
1930+
column.get_data_slice(table.len() as usize).into(),
1931+
column.get_added_ticks_slice(table.len() as usize).into(),
1932+
column.get_changed_ticks_slice(table.len() as usize).into(),
19411933
column
1942-
.get_changed_by_slice(table.entity_count() as usize)
1934+
.get_changed_by_slice(table.len() as usize)
19431935
.map(Into::into),
19441936
));
19451937
// SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table

crates/bevy_ecs/src/query/iter.rs

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
148148
// SAFETY: Matched table IDs are guaranteed to still exist.
149149
let table = unsafe { self.tables.get(table_id).debug_checked_unwrap() };
150150

151-
let range = range.unwrap_or(0..table.entity_count());
151+
let range = range.unwrap_or(table.table_rows());
152152
accum =
153153
// SAFETY:
154154
// - The fetched table matches both D and F
@@ -163,11 +163,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
163163
// SAFETY: Matched table IDs are guaranteed to still exist.
164164
let table = unsafe { self.tables.get(archetype.table_id()).debug_checked_unwrap() };
165165

166-
let range = range.unwrap_or(0..archetype.len());
166+
let range = range.unwrap_or(archetype.archetype_rows());
167167

168168
// When an archetype and its table have equal entity counts, dense iteration can be safely used.
169169
// this leverages cache locality to optimize performance.
170-
if table.entity_count() == archetype.len() {
170+
if table.entity_count() == archetype.entity_count() {
171171
accum =
172172
// SAFETY:
173173
// - The fetched archetype matches both D and F
@@ -345,7 +345,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
345345
}
346346
let table = self.tables.get(archetype.table_id()).debug_checked_unwrap();
347347
debug_assert!(
348-
archetype.len() == table.entity_count(),
348+
archetype.entity_count() == table.entity_count(),
349349
"archetype and its table must have the same length. "
350350
);
351351

@@ -930,14 +930,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F>
930930
{
931931
let mut accum = init;
932932
// Empty any remaining uniterated values from the current table/archetype
933-
while self.cursor.current_row != self.cursor.current_len {
933+
while !self.cursor.current_range.is_empty() {
934934
let Some(item) = self.next() else { break };
935935
accum = func(accum, item);
936936
}
937937

938938
for id in self.cursor.storage_id_iter.clone().copied() {
939939
// SAFETY:
940-
// - The range(None) is equivalent to [0, storage.entity_count)
940+
// - The range(None) is equivalent to storage.rows
941941
accum = unsafe { self.fold_over_storage_range(accum, &mut func, id, None) };
942942
}
943943
accum
@@ -2365,10 +2365,8 @@ struct QueryIterationCursor<'w, 's, D: QueryData, F: QueryFilter> {
23652365
archetype_entities: &'w [ArchetypeEntity],
23662366
fetch: D::Fetch<'w>,
23672367
filter: F::Fetch<'w>,
2368-
// length of the table or length of the archetype, depending on whether both `D`'s and `F`'s fetches are dense
2369-
current_len: u32,
2370-
// either table row or archetype index, depending on whether both `D`'s and `F`'s fetches are dense
2371-
current_row: u32,
2368+
// remaining range of the table or archetype being iterated over, depending on whether both `D`'s and `F`'s fetches are dense
2369+
current_range: Range<u32>,
23722370
}
23732371

23742372
impl<D: QueryData, F: QueryFilter> Clone for QueryIterationCursor<'_, '_, D, F> {
@@ -2380,8 +2378,7 @@ impl<D: QueryData, F: QueryFilter> Clone for QueryIterationCursor<'_, '_, D, F>
23802378
archetype_entities: self.archetype_entities,
23812379
fetch: self.fetch.clone(),
23822380
filter: self.filter.clone(),
2383-
current_len: self.current_len,
2384-
current_row: self.current_row,
2381+
current_range: self.current_range.clone(),
23852382
}
23862383
}
23872384
}
@@ -2420,8 +2417,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
24202417
archetype_entities: &[],
24212418
storage_id_iter: query_state.matched_storage_ids.iter(),
24222419
is_dense: query_state.is_dense,
2423-
current_len: 0,
2424-
current_row: 0,
2420+
current_range: 0..0,
24252421
}
24262422
}
24272423

@@ -2433,8 +2429,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
24332429
table_entities: self.table_entities,
24342430
archetype_entities: self.archetype_entities,
24352431
storage_id_iter: self.storage_id_iter.clone(),
2436-
current_len: self.current_len,
2437-
current_row: self.current_row,
2432+
current_range: 0..0,
24382433
}
24392434
}
24402435

@@ -2445,8 +2440,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
24452440
/// dropped to prevent aliasing mutable references.
24462441
#[inline]
24472442
unsafe fn peek_last(&mut self, query_state: &'s QueryState<D, F>) -> Option<D::Item<'w, 's>> {
2448-
if self.current_row > 0 {
2449-
let index = self.current_row - 1;
2443+
if self.current_range.start > 0 {
2444+
let index = self.current_range.start - 1;
24502445
if self.is_dense {
24512446
// SAFETY: This must have been called previously in `next` as `current_row > 0`
24522447
let entity = unsafe { self.table_entities.get_unchecked(index as usize) };
@@ -2494,9 +2489,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
24942489
unsafe { ids.map(|id| tables[id.table_id].entity_count()).sum() }
24952490
} else {
24962491
// SAFETY: The if check ensures that storage_id_iter stores ArchetypeIds
2497-
unsafe { ids.map(|id| archetypes[id.archetype_id].len()).sum() }
2492+
unsafe {
2493+
ids.map(|id| archetypes[id.archetype_id].entity_count())
2494+
.sum()
2495+
}
24982496
};
2499-
remaining_matched + self.current_len - self.current_row
2497+
remaining_matched + self.current_range.len() as u32
25002498
}
25012499

25022500
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
@@ -2517,7 +2515,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
25172515
if self.is_dense {
25182516
loop {
25192517
// we are on the beginning of the query, or finished processing a table, so skip to the next
2520-
if self.current_row == self.current_len {
2518+
let Some(next) = self.current_range.next() else {
25212519
let table_id = self.storage_id_iter.next()?.table_id;
25222520
let table = tables.get(table_id).debug_checked_unwrap();
25232521
if table.is_empty() {
@@ -2530,18 +2528,17 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
25302528
F::set_table(&mut self.filter, &query_state.filter_state, table);
25312529
}
25322530
self.table_entities = table.entities();
2533-
self.current_len = table.entity_count();
2534-
self.current_row = 0;
2535-
}
2531+
self.current_range = table.table_rows();
2532+
2533+
continue;
2534+
};
25362535

25372536
// SAFETY: set_table was called prior.
25382537
// `current_row` is a table row in range of the current table, because if it was not, then the above would have been executed.
2539-
let entity =
2540-
unsafe { self.table_entities.get_unchecked(self.current_row as usize) };
2538+
let entity = unsafe { self.table_entities.get_unchecked(next as usize) };
25412539
// SAFETY: The row is less than the u32 len, so it must not be max.
2542-
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(self.current_row)) };
2540+
let row = unsafe { TableRow::new(NonMaxU32::new_unchecked(next)) };
25432541
if !F::filter_fetch(&query_state.filter_state, &mut self.filter, *entity, row) {
2544-
self.current_row += 1;
25452542
continue;
25462543
}
25472544

@@ -2553,12 +2550,11 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
25532550
let item =
25542551
unsafe { D::fetch(&query_state.fetch_state, &mut self.fetch, *entity, row) };
25552552

2556-
self.current_row += 1;
25572553
return Some(item);
25582554
}
25592555
} else {
25602556
loop {
2561-
if self.current_row == self.current_len {
2557+
let Some(next) = self.current_range.next() else {
25622558
let archetype_id = self.storage_id_iter.next()?.archetype_id;
25632559
let archetype = archetypes.get(archetype_id).debug_checked_unwrap();
25642560
if archetype.is_empty() {
@@ -2582,23 +2578,21 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
25822578
);
25832579
}
25842580
self.archetype_entities = archetype.entities();
2585-
self.current_len = archetype.len();
2586-
self.current_row = 0;
2587-
}
2581+
self.current_range = archetype.archetype_rows();
2582+
2583+
continue;
2584+
};
25882585

25892586
// SAFETY: set_archetype was called prior.
25902587
// `current_row` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed.
2591-
let archetype_entity = unsafe {
2592-
self.archetype_entities
2593-
.get_unchecked(self.current_row as usize)
2594-
};
2588+
let archetype_entity =
2589+
unsafe { self.archetype_entities.get_unchecked(next as usize) };
25952590
if !F::filter_fetch(
25962591
&query_state.filter_state,
25972592
&mut self.filter,
25982593
archetype_entity.id(),
25992594
archetype_entity.table_row(),
26002595
) {
2601-
self.current_row += 1;
26022596
continue;
26032597
}
26042598

@@ -2615,7 +2609,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
26152609
archetype_entity.table_row(),
26162610
)
26172611
};
2618-
self.current_row += 1;
2612+
26192613
return Some(item);
26202614
}
26212615
}

crates/bevy_ecs/src/query/par_iter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryParIter<'w, 's, D, F> {
144144
let archetypes = &self.world.archetypes();
145145
id_iter
146146
// SAFETY: The if check ensures that matched_storage_ids stores ArchetypeIds
147-
.map(|id| unsafe { archetypes[id.archetype_id].len() })
147+
.map(|id| unsafe { archetypes[id.archetype_id].entity_count() })
148148
.max()
149149
}
150150
.map(|v| v as usize)

crates/bevy_ecs/src/query/state.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
14171417

14181418
bevy_tasks::ComputeTaskPool::get().scope(|scope| {
14191419
// SAFETY: We only access table data that has been registered in `self.component_access`.
1420+
1421+
use core::ops::Range;
14201422
let tables = unsafe { &world.storages().tables };
14211423
let archetypes = world.archetypes();
14221424
let mut batch_queue = ArrayVec::new();
@@ -1444,8 +1446,9 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
14441446
};
14451447

14461448
// submit single storage larger than batch_size
1447-
let submit_single = |count, storage_id: StorageId| {
1448-
for offset in (0..count).step_by(batch_size as usize) {
1449+
let submit_single = |range: Range<u32>, storage_id: StorageId| {
1450+
let count = range.len() as u32;
1451+
for offset in range.step_by(batch_size as usize) {
14491452
let mut func = func.clone();
14501453
let init_accum = init_accum.clone();
14511454
let len = batch_size.min(count - offset);
@@ -1461,29 +1464,29 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
14611464
}
14621465
};
14631466

1464-
let storage_entity_count = |storage_id: StorageId| -> u32 {
1467+
let storage_entity_rows = |storage_id: StorageId| -> Range<u32> {
14651468
if self.is_dense {
1466-
tables[storage_id.table_id].entity_count()
1469+
tables[storage_id.table_id].table_rows()
14671470
} else {
1468-
archetypes[storage_id.archetype_id].len()
1471+
archetypes[storage_id.archetype_id].archetype_rows()
14691472
}
14701473
};
14711474

14721475
for storage_id in &self.matched_storage_ids {
1473-
let count = storage_entity_count(*storage_id);
1476+
let range = storage_entity_rows(*storage_id);
14741477

14751478
// skip empty storage
1476-
if count == 0 {
1479+
if range.is_empty() {
14771480
continue;
14781481
}
14791482
// immediately submit large storage
1480-
if count >= batch_size {
1481-
submit_single(count, *storage_id);
1483+
if range.len() as u32 >= batch_size {
1484+
submit_single(range, *storage_id);
14821485
continue;
14831486
}
14841487
// merge small storage
14851488
batch_queue.push(*storage_id);
1486-
queue_entity_count += count;
1489+
queue_entity_count += range.len() as u32;
14871490

14881491
// submit batch_queue
14891492
if queue_entity_count >= batch_size || batch_queue.is_full() {

0 commit comments

Comments
 (0)