From 5b4edeec5c4d8d15a2e742d24d76803f883b24f0 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 14:54:35 -0700 Subject: [PATCH 01/42] Get started --- crates/bevy_ecs/src/query/fetch.rs | 213 ++++++---------------------- crates/bevy_ecs/src/query/filter.rs | 74 +++++----- crates/bevy_ecs/src/query/state.rs | 44 +++--- 3 files changed, 100 insertions(+), 231 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index ff2a629277502..48661574affd7 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -374,41 +374,11 @@ pub trait Fetch<'world>: Sized { /// [`Self::State`] this was initialized with. unsafe fn set_table(&mut self, state: &Self::State, table: &'world Table); - /// Fetch [`Self::Item`] for the given `archetype_index` in the current [`Archetype`]. This must - /// always be called after [`Fetch::set_archetype`] with an `archetype_index` in the range of - /// the current [`Archetype`] - /// - /// # Safety - /// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range - /// of the current archetype - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item; - - /// Fetch [`Self::Item`] for the given `table_row` in the current [`Table`]. This must always be - /// called after [`Fetch::set_table`] with a `table_row` in the range of the current [`Table`] - /// - /// # Safety - /// - /// Must always be called _after_ [`Fetch::set_table`]. `table_row` must be in the range of the - /// current table - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item; + unsafe fn fetch(&mut self, entity: &Entity, table_index: &usize) -> Self::Item; - /// # Safety - /// - /// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range - /// of the current archetype. #[allow(unused_variables)] - #[inline] - unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool { - true - } - - /// # Safety - /// - /// Must always be called _after_ [`Fetch::set_table`]. `table_row` must be in the range of the - /// current table. - #[allow(unused_variables)] - #[inline] - unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool { + #[inline(always)] + unsafe fn filter_fetch(&mut self, entity: &Entity, table_index: &usize) -> bool { true } } @@ -454,7 +424,7 @@ impl WorldQuery for Entity { #[doc(hidden)] #[derive(Clone)] pub struct EntityFetch<'w> { - entities: Option>, + marker: PhantomData<&'w Entity>, } /// SAFETY: access is read only @@ -510,7 +480,7 @@ impl<'w> Fetch<'w> for EntityFetch<'w> { _last_change_tick: u32, _change_tick: u32, ) -> EntityFetch<'w> { - EntityFetch { entities: None } + EntityFetch { marker: PhantomData } } #[inline] @@ -520,24 +490,14 @@ impl<'w> Fetch<'w> for EntityFetch<'w> { archetype: &'w Archetype, _tables: &Tables, ) { - self.entities = Some(archetype.entities().into()); - } - - #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, table: &'w Table) { - self.entities = Some(table.entities().into()); } #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - let entities = self.entities.unwrap_or_else(|| debug_checked_unreachable()); - *entities.get(table_row) - } + unsafe fn set_table(&mut self, _state: &Self::State, table: &'w Table) {} - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - let entities = self.entities.unwrap_or_else(|| debug_checked_unreachable()); - *entities.get(archetype_index) + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + *entity } } @@ -691,38 +651,27 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { ); } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let (entity_table_rows, table_components) = self - .entity_table_rows - .zip(self.table_components) + let components = self + .table_components .unwrap_or_else(|| debug_checked_unreachable()); - let table_row = *entity_table_rows.get(archetype_index); - table_components.get(table_row).deref() + components.get(*table_row).deref() } StorageType::SparseSet => { let (entities, sparse_set) = self .entities .zip(self.sparse_set) .unwrap_or_else(|| debug_checked_unreachable()); - let entity = *entities.get(archetype_index); sparse_set - .get(entity) + .get(*entity) .unwrap_or_else(|| debug_checked_unreachable()) .deref::() } } } - - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - let components = self - .table_components - .unwrap_or_else(|| debug_checked_unreachable()); - components.get(table_row).deref() - } } impl WorldQuery for &mut T { @@ -903,15 +852,14 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { self.table_ticks = Some(column.get_ticks_slice().into()); } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let (entity_table_rows, (table_components, table_ticks)) = self - .entity_table_rows - .zip(self.table_components.zip(self.table_ticks)) + let (table_components, table_ticks) = self + .table_components + .zip(self.table_ticks) .unwrap_or_else(|| debug_checked_unreachable()); - let table_row = *entity_table_rows.get(archetype_index); Mut { value: table_components.get(table_row).deref_mut(), ticks: Ticks { @@ -941,22 +889,6 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { } } } - - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - let (table_components, table_ticks) = self - .table_components - .zip(self.table_ticks) - .unwrap_or_else(|| debug_checked_unreachable()); - Mut { - value: table_components.get(table_row).deref_mut(), - ticks: Ticks { - component_ticks: table_ticks.get(table_row).deref_mut(), - change_tick: self.change_tick, - last_change_tick: self.last_change_tick, - }, - } - } } impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { @@ -1022,16 +954,14 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { ); } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let (entity_table_rows, table_components) = self - .entity_table_rows - .zip(self.table_components) + let components = self + .table_components .unwrap_or_else(|| debug_checked_unreachable()); - let table_row = *entity_table_rows.get(archetype_index); - table_components.get(table_row).deref() + components.get(table_row).deref() } StorageType::SparseSet => { let (entities, sparse_set) = self @@ -1046,14 +976,6 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { } } } - - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - let components = self - .table_components - .unwrap_or_else(|| debug_checked_unreachable()); - components.get(table_row).deref() - } } impl WorldQuery for Option { @@ -1167,19 +1089,10 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { } } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { - if self.matches { - Some(self.fetch.archetype_fetch(archetype_index)) - } else { - None - } - } - - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { if self.matches { - Some(self.fetch.table_fetch(table_row)) + Some(self.fetch.fetch(archetype_index)) } else { None } @@ -1410,14 +1323,10 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { ); } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> Self::Item { + #[inline(always)] + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let entity_table_rows = self - .entity_table_rows - .unwrap_or_else(|| debug_checked_unreachable()); - let table_row = *entity_table_rows.get(archetype_index); ChangeTrackers { component_ticks: { let table_ticks = self @@ -1448,21 +1357,6 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { } } } - - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> Self::Item { - ChangeTrackers { - component_ticks: { - let table_ticks = self - .table_ticks - .unwrap_or_else(|| debug_checked_unreachable()); - table_ticks.get(table_row).read() - }, - marker: PhantomData, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - } - } } macro_rules! impl_tuple_fetch { @@ -1504,32 +1398,17 @@ macro_rules! impl_tuple_fetch { $($name.set_table($state, _table);)* } - #[inline] + #[inline(always)] #[allow(clippy::unused_unit)] - unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { let ($($name,)*) = self; - ($($name.table_fetch(_table_row),)*) + ($($name.fetch(_entity, _table_row),)*) } - #[inline] - #[allow(clippy::unused_unit)] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item { - let ($($name,)*) = self; - ($($name.archetype_fetch(_archetype_index),)*) - } - - #[allow(unused_variables)] - #[inline] - unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool { - let ($($name,)*) = self; - true $(&& $name.table_filter_fetch(table_row))* - } - - #[allow(unused_variables)] - #[inline] - unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool { + #[inline(always)] + unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> bool { let ($($name,)*) = self; - true $(&& $name.archetype_filter_fetch(archetype_index))* + true $(&& $name.filter_fetch(_entity, _table_row))* } } @@ -1637,21 +1516,12 @@ macro_rules! impl_anytuple_fetch { )* } - #[inline] - #[allow(clippy::unused_unit)] - unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item { - let ($($name,)*) = &mut self.0; - ($( - $name.1.then(|| $name.0.table_fetch(_table_row)), - )*) - } - - #[inline] + #[inline(always)] #[allow(clippy::unused_unit)] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { let ($($name,)*) = &mut self.0; ($( - $name.1.then(|| $name.0.archetype_fetch(_archetype_index)), + $name.1.then(|| $name.0.fetch(_entity, _table_row)), )*) } } @@ -1776,8 +1646,5 @@ impl<'w, State: FetchState> Fetch<'w> for NopFetch { unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} #[inline(always)] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item {} - - #[inline(always)] - unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item {} + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item {} } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 36dc70844eff8..8700fe923b213 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -141,11 +141,8 @@ impl<'w, T: Component> Fetch<'w> for WithFetch { ) { } - #[inline] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) {} - - #[inline] - unsafe fn table_fetch(&mut self, _table_row: usize) {} + #[inline(always)] + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) {} } // SAFETY: no component access or archetype component access @@ -284,11 +281,8 @@ impl<'w, T: Component> Fetch<'w> for WithoutFetch { ) { } - #[inline] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) {} - - #[inline] - unsafe fn table_fetch(&mut self, _table_row: usize) {} + #[inline(always)] + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) {} } // SAFETY: no component access or archetype component access @@ -409,26 +403,17 @@ macro_rules! impl_query_filter_tuple { )* } - #[inline] - unsafe fn table_fetch(&mut self, table_row: usize) -> bool { + #[inline(always)] + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { let ($($filter,)*) = &mut self.0; - false $(|| ($filter.matches && $filter.fetch.table_filter_fetch(table_row)))* + false $(|| ($filter.matches && $filter.fetch.filter_fetch(_entity, _table_row)))* } - #[inline] - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool { + #[inline(always)] + unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + // Explicitly repeated code here to avoid potential poor inlining let ($($filter,)*) = &mut self.0; - false $(|| ($filter.matches && $filter.fetch.archetype_filter_fetch(archetype_index)))* - } - - #[inline] - unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool { - self.table_fetch(table_row) - } - - #[inline] - unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool { - self.archetype_fetch(archetype_index) + false $(|| ($filter.matches && $filter.fetch.filter_fetch(_entity, _table_row)))* } } @@ -620,14 +605,10 @@ macro_rules! impl_tick_filter { } } - unsafe fn table_fetch(&mut self, table_row: usize) -> bool { - $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick) - } - - unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool { + #[inline(always)] + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let table_row = *self.entity_table_rows.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index); $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick) } StorageType::SparseSet => { @@ -644,14 +625,25 @@ macro_rules! impl_tick_filter { } } - #[inline] - unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool { - self.table_fetch(table_row) - } - - #[inline] - unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool { - self.archetype_fetch(archetype_index) + #[inline(always)] + unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + // Explicitly repeated code here to avoid potential poor inlining + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick) + } + StorageType::SparseSet => { + let entity = *self.entities.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index); + let ticks = self + .sparse_set + .unwrap_or_else(|| debug_checked_unreachable()) + .get_ticks(entity) + .map(|ticks| &*ticks.get()) + .cloned() + .unwrap(); + $is_detected(&ticks, self.last_change_tick, self.change_tick) + } + } } } @@ -713,7 +705,7 @@ impl_tick_filter!( impl_tick_filter!( /// A filter on a component that only retains results added or mutably dereferenced after the system last ran. - /// + /// /// A common use for this filter is avoiding redundant work when values have not changed. /// /// **Note** that simply *mutably dereferencing* a component is considered a change ([`DerefMut`](std::ops::DerefMut)). diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index ae23c1c512e8e..fc83957e02e8f 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -363,8 +363,8 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables); filter.set_archetype(&self.filter_state, archetype, &world.storages().tables); - if filter.archetype_filter_fetch(location.index) { - Ok(fetch.archetype_fetch(location.index)) + if filter.filter_fetch(&entity, &location.index) { + Ok(fetch.fetch(&entity, &location.index)) } else { Err(QueryEntityError::QueryDoesNotMatch(entity)) } @@ -799,12 +799,14 @@ impl QueryState { fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); - for table_index in 0..table.len() { - if !filter.table_filter_fetch(table_index) { + let entities = table.entities(); + for row in 0..table.len() { + let entity = entities.get_unchecked(row); + if !filter.filter_fetch(entity, &row) { continue; } - let item = fetch.table_fetch(table_index); - func(item); + let item = ; + func(ifetch.fetch(entity, &row)); } } } else { @@ -815,11 +817,15 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - for archetype_index in 0..archetype.len() { - if !filter.archetype_filter_fetch(archetype_index) { + let rows = archetype.entity_table_rows(); + let entities = archetype.entities(); + for idx in 0..archetype.len() { + let row = rows.get_unchecked(idx); + let entity = entities.get_unchecked(idx); + if !filter.filter_fetch(entity, row) { continue; } - func(fetch.archetype_fetch(archetype_index)); + func(fetch.fetch(entity, row)); } } } @@ -872,12 +878,12 @@ impl QueryState { let table = &tables[*table_id]; fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); - for table_index in offset..offset + len { - if !filter.table_filter_fetch(table_index) { + for row in 0..table.len() { + let entity = entities.get_unchecked(row); + if !filter.filter_fetch(entity, &row) { continue; } - let item = fetch.table_fetch(table_index); - func(item); + func(fetch.fetch(entity, &row)); } }; #[cfg(feature = "trace")] @@ -911,15 +917,19 @@ impl QueryState { change_tick, ); let tables = &world.storages().tables; - let archetype = &world.archetypes[*archetype_id]; + let archetype = &archetypes[*archetype_id]; fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - for archetype_index in offset..offset + len { - if !filter.archetype_filter_fetch(archetype_index) { + let rows = archetype.entity_table_rows(); + let entities = archetype.entities(); + for idx in 0..archetype.len() { + let row = rows.get_unchecked(idx); + let entity = entities.get_unchecked(idx); + if !filter.filter_fetch(entity, row) { continue; } - func(fetch.archetype_fetch(archetype_index)); + func(fetch.fetch(entity, row)); } }; From 08178ac7423b004525a09ecff4cc22b96c4ceec5 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 15:54:44 -0700 Subject: [PATCH 02/42] get it to compile --- crates/bevy_ecs/src/query/fetch.rs | 59 +++++++++++++---------------- crates/bevy_ecs/src/query/filter.rs | 12 +++--- crates/bevy_ecs/src/query/iter.rs | 33 ++++++++++++---- crates/bevy_ecs/src/query/state.rs | 14 +++---- 4 files changed, 64 insertions(+), 54 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 48661574affd7..d3b077a51bd3c 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -480,23 +480,25 @@ impl<'w> Fetch<'w> for EntityFetch<'w> { _last_change_tick: u32, _change_tick: u32, ) -> EntityFetch<'w> { - EntityFetch { marker: PhantomData } + EntityFetch { + marker: PhantomData, + } } #[inline] unsafe fn set_archetype( &mut self, _state: &Self::State, - archetype: &'w Archetype, + _archetype: &'w Archetype, _tables: &Tables, ) { } #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, table: &'w Table) {} + unsafe fn set_table(&mut self, _state: &Self::State, _table: &'w Table) {} #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: &Entity, _table_row: &usize) -> Self::Item { *entity } } @@ -661,7 +663,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { components.get(*table_row).deref() } StorageType::SparseSet => { - let (entities, sparse_set) = self + let (_, sparse_set) = self .entities .zip(self.sparse_set) .unwrap_or_else(|| debug_checked_unreachable()); @@ -856,6 +858,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { + let table_row = *table_row; let (table_components, table_ticks) = self .table_components .zip(self.table_ticks) @@ -870,13 +873,12 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { } } StorageType::SparseSet => { - let (entities, sparse_set) = self + let (_, sparse_set) = self .entities .zip(self.sparse_set) .unwrap_or_else(|| debug_checked_unreachable()); - let entity = *entities.get(archetype_index); let (component, component_ticks) = sparse_set - .get_with_ticks(entity) + .get_with_ticks(*entity) .unwrap_or_else(|| debug_checked_unreachable()); Mut { value: component.assert_unique().deref_mut(), @@ -961,16 +963,15 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { let components = self .table_components .unwrap_or_else(|| debug_checked_unreachable()); - components.get(table_row).deref() + components.get(*table_row).deref() } StorageType::SparseSet => { - let (entities, sparse_set) = self + let (_, sparse_set) = self .entities .zip(self.sparse_set) .unwrap_or_else(|| debug_checked_unreachable()); - let entity = *entities.get(archetype_index); sparse_set - .get(entity) + .get(*entity) .unwrap_or_else(|| debug_checked_unreachable()) .deref::() } @@ -1091,11 +1092,7 @@ impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { #[inline(always)] unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { - if self.matches { - Some(self.fetch.fetch(archetype_index)) - } else { - None - } + self.matches.then(|| self.fetch.fetch(entity, table_row)) } } @@ -1326,27 +1323,23 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { #[inline(always)] unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { - StorageType::Table => { - ChangeTrackers { - component_ticks: { - let table_ticks = self - .table_ticks - .unwrap_or_else(|| debug_checked_unreachable()); - table_ticks.get(table_row).read() - }, - marker: PhantomData, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - } - } + StorageType::Table => ChangeTrackers { + component_ticks: { + let table_ticks = self + .table_ticks + .unwrap_or_else(|| debug_checked_unreachable()); + table_ticks.get(*table_row).read() + }, + marker: PhantomData, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, StorageType::SparseSet => { - let entities = self.entities.unwrap_or_else(|| debug_checked_unreachable()); - let entity = *entities.get(archetype_index); ChangeTrackers { component_ticks: self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(entity) + .get_ticks(*entity) .map(|ticks| &*ticks.get()) .cloned() .unwrap_or_else(|| debug_checked_unreachable()), diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 8700fe923b213..6eddd304e64b6 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -606,17 +606,16 @@ macro_rules! impl_tick_filter { } #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick) + $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(*table_row)).deref(), self.last_change_tick, self.change_tick) } StorageType::SparseSet => { - let entity = *self.entities.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index); let ticks = self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(entity) + .get_ticks(*entity) .map(|ticks| &*ticks.get()) .cloned() .unwrap(); @@ -630,14 +629,13 @@ macro_rules! impl_tick_filter { // Explicitly repeated code here to avoid potential poor inlining match T::Storage::STORAGE_TYPE { StorageType::Table => { - $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick) + $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(*_table_row)).deref(), self.last_change_tick, self.change_tick) } StorageType::SparseSet => { - let entity = *self.entities.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index); let ticks = self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(entity) + .get_ticks(*_entity) .map(|ticks| &*ticks.get()) .cloned() .unwrap(); diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 5b5094f647bcc..fa1b152c15f03 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -1,5 +1,6 @@ use crate::{ archetype::{ArchetypeId, Archetypes}, + entity::Entity, query::{Fetch, QueryState, WorldQuery}, storage::{TableId, Tables}, world::World, @@ -254,6 +255,8 @@ where struct QueryIterationCursor<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, + entities: Option<&'s [Entity]>, + rows: Option<&'s [usize]>, fetch: QF, filter: QueryFetch<'w, F>, current_len: usize, @@ -270,6 +273,8 @@ where Self { table_id_iter: self.table_id_iter.clone(), archetype_id_iter: self.archetype_id_iter.clone(), + entities: None, + rows: None, fetch: self.fetch.clone(), filter: self.filter.clone(), current_len: self.current_len, @@ -319,6 +324,8 @@ where QueryIterationCursor { fetch, filter, + entities: None, + rows: None, table_id_iter: query_state.matched_table_ids.iter(), archetype_id_iter: query_state.matched_archetype_ids.iter(), current_len: 0, @@ -331,10 +338,13 @@ where #[inline] unsafe fn peek_last(&mut self) -> Option { if self.current_index > 0 { + let index = self.current_index - 1; + let entity = self.entities.unwrap().get_unchecked(index); if Self::IS_DENSE { - Some(self.fetch.table_fetch(self.current_index - 1)) + Some(self.fetch.fetch(entity, &index)) } else { - Some(self.fetch.archetype_fetch(self.current_index - 1)) + let row = self.rows.unwrap().get_unchecked(index); + Some(self.fetch.fetch(entity, row)) } } else { None @@ -357,18 +367,21 @@ where let table = &tables[*table_id]; self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); + // This borrow is valid for the lifetime of the state, but the compiler + // can't prove that. + self.entities = Some(std::mem::transmute(table.entities())); self.current_len = table.len(); self.current_index = 0; continue; } - if !self.filter.table_filter_fetch(self.current_index) { + let entity = self.entities.unwrap().get_unchecked(self.current_index); + if !self.filter.filter_fetch(entity, &self.current_index) { self.current_index += 1; continue; } - let item = self.fetch.table_fetch(self.current_index); - + let item = self.fetch.fetch(entity, &self.current_index); self.current_index += 1; return Some(item); } @@ -381,17 +394,23 @@ where .set_archetype(&query_state.fetch_state, archetype, tables); self.filter .set_archetype(&query_state.filter_state, archetype, tables); + // These borrows are valid for the lifetime of the state, but the compiler + // can't prove that. + self.entities = Some(std::mem::transmute(archetype.entities())); + self.rows = Some(std::mem::transmute(archetype.entity_table_rows())); self.current_len = archetype.len(); self.current_index = 0; continue; } - if !self.filter.archetype_filter_fetch(self.current_index) { + let entity = self.entities.unwrap().get_unchecked(self.current_index); + let row = self.rows.unwrap().get_unchecked(self.current_index); + if !self.filter.filter_fetch(entity, row) { self.current_index += 1; continue; } - let item = self.fetch.archetype_fetch(self.current_index); + let item = self.fetch.fetch(entity, row); self.current_index += 1; return Some(item); } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index fc83957e02e8f..32a02a2dd45b4 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -805,8 +805,7 @@ impl QueryState { if !filter.filter_fetch(entity, &row) { continue; } - let item = ; - func(ifetch.fetch(entity, &row)); + func(fetch.fetch(entity, &row)); } } } else { @@ -876,9 +875,10 @@ impl QueryState { ); let tables = &world.storages().tables; let table = &tables[*table_id]; + let entities = table.entities(); fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); - for row in 0..table.len() { + for row in offset..offset + len { let entity = entities.get_unchecked(row); if !filter.filter_fetch(entity, &row) { continue; @@ -917,13 +917,13 @@ impl QueryState { change_tick, ); let tables = &world.storages().tables; - let archetype = &archetypes[*archetype_id]; + let archetype = &world.archetypes[*archetype_id]; + let rows = archetype.entity_table_rows(); + let entities = archetype.entities(); fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - let rows = archetype.entity_table_rows(); - let entities = archetype.entities(); - for idx in 0..archetype.len() { + for idx in offset..offset + len { let row = rows.get_unchecked(idx); let entity = entities.get_unchecked(idx); if !filter.filter_fetch(entity, row) { From 550ebfe0c14ac8f116abcf491111ba9216b1fa09 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 15:58:09 -0700 Subject: [PATCH 03/42] Use dense iteration when any fetch is dense --- crates/bevy_ecs/src/query/fetch.rs | 4 ++-- crates/bevy_ecs/src/query/filter.rs | 2 +- crates/bevy_ecs/src/query/iter.rs | 2 +- crates/bevy_ecs/src/query/state.rs | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index d3b077a51bd3c..bb0831551b9f8 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1373,7 +1373,7 @@ macro_rules! impl_tuple_fetch { ($($name::init(_world, $name, _last_change_tick, _change_tick),)*) } - const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; + const IS_DENSE: bool = true $(|| $name::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; @@ -1481,7 +1481,7 @@ macro_rules! impl_anytuple_fetch { AnyOf(($(($name::init(_world, $name, _last_change_tick, _change_tick), false),)*)) } - const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; + const IS_DENSE: bool = true $(|| $name::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 6eddd304e64b6..c954fd9327778 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -366,7 +366,7 @@ macro_rules! impl_query_filter_tuple { type State = Or<($(<$filter as Fetch<'w>>::State,)*)>; type Item = bool; - const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*; + const IS_DENSE: bool = true $(|| $filter::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*; diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index fa1b152c15f03..08459972daf44 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -288,7 +288,7 @@ impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> QueryIterationCursor<'w, 's, Q, Q where QF: Fetch<'w, State = Q::State>, { - const IS_DENSE: bool = QF::IS_DENSE && >::IS_DENSE; + const IS_DENSE: bool = QF::IS_DENSE || >::IS_DENSE; unsafe fn init_empty( world: &'w World, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 32a02a2dd45b4..db327288ec26c 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -792,7 +792,7 @@ impl QueryState { change_tick, ); - if >::IS_DENSE && >::IS_DENSE { + if >::IS_DENSE || >::IS_DENSE { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; @@ -856,7 +856,7 @@ impl QueryState { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual task_pool.scope(|scope| { - if QF::IS_DENSE && >::IS_DENSE { + if QF::IS_DENSE || >::IS_DENSE { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; From 24c6d59e960a157a56b83f26e5ae95bb7650c0f0 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 16:43:27 -0700 Subject: [PATCH 04/42] Remove internal entity_table_rows. No longer needed. --- crates/bevy_ecs/src/query/fetch.rs | 16 ---------------- crates/bevy_ecs/src/query/filter.rs | 4 ---- 2 files changed, 20 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index bb0831551b9f8..c9d47fb0c0f16 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -564,7 +564,6 @@ unsafe impl FetchState for ReadState { pub struct ReadFetch<'w, T> { // T::Storage = TableStorage table_components: Option>>, - entity_table_rows: Option>, // T::Storage = SparseStorage entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, @@ -574,7 +573,6 @@ impl Clone for ReadFetch<'_, T> { fn clone(&self) -> Self { Self { table_components: self.table_components, - entity_table_rows: self.entity_table_rows, entities: self.entities, sparse_set: self.sparse_set, } @@ -611,7 +609,6 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { ) -> ReadFetch<'w, T> { ReadFetch { table_components: None, - entity_table_rows: None, entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world @@ -632,7 +629,6 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { ) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.entity_table_rows = Some(archetype.entity_table_rows().into()); let column = tables[archetype.table_id()] .get_column(state.component_id) .unwrap(); @@ -690,7 +686,6 @@ pub struct WriteFetch<'w, T> { // T::Storage = TableStorage table_components: Option>>, table_ticks: Option>>, - entity_table_rows: Option>, // T::Storage = SparseStorage entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, @@ -705,7 +700,6 @@ impl Clone for WriteFetch<'_, T> { table_components: self.table_components, table_ticks: self.table_ticks, entities: self.entities, - entity_table_rows: self.entity_table_rows, sparse_set: self.sparse_set, last_change_tick: self.last_change_tick, change_tick: self.change_tick, @@ -717,7 +711,6 @@ impl Clone for WriteFetch<'_, T> { pub struct ReadOnlyWriteFetch<'w, T> { // T::Storage = TableStorage table_components: Option>>, - entity_table_rows: Option>, // T::Storage = SparseStorage entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, @@ -731,7 +724,6 @@ impl Clone for ReadOnlyWriteFetch<'_, T> { Self { table_components: self.table_components, entities: self.entities, - entity_table_rows: self.entity_table_rows, sparse_set: self.sparse_set, } } @@ -813,7 +805,6 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { Self { table_components: None, entities: None, - entity_table_rows: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -836,7 +827,6 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { ) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.entity_table_rows = Some(archetype.entity_table_rows().into()); let column = tables[archetype.table_id()] .get_column(state.component_id) .unwrap(); @@ -915,7 +905,6 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { Self { table_components: None, entities: None, - entity_table_rows: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -935,7 +924,6 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { ) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.entity_table_rows = Some(archetype.entity_table_rows().into()); let column = tables[archetype.table_id()] .get_column(state.component_id) .unwrap(); @@ -1221,7 +1209,6 @@ unsafe impl FetchState for ChangeTrackersState { pub struct ChangeTrackersFetch<'w, T> { // T::Storage = TableStorage table_ticks: Option>>, - entity_table_rows: Option>, // T::Storage = SparseStorage entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, @@ -1235,7 +1222,6 @@ impl Clone for ChangeTrackersFetch<'_, T> { fn clone(&self) -> Self { Self { table_ticks: self.table_ticks, - entity_table_rows: self.entity_table_rows, entities: self.entities, sparse_set: self.sparse_set, marker: self.marker, @@ -1276,7 +1262,6 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { ChangeTrackersFetch { table_ticks: None, entities: None, - entity_table_rows: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -1299,7 +1284,6 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { ) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.entity_table_rows = Some(archetype.entity_table_rows().into()); let column = tables[archetype.table_id()] .get_column(state.component_id) .unwrap(); diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index c954fd9327778..0f43bf2923c95 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -497,7 +497,6 @@ macro_rules! impl_tick_filter { $(#[$fetch_meta])* pub struct $fetch_name<'w, T> { table_ticks: Option>>, - entity_table_rows: Option>, marker: PhantomData, entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, @@ -572,7 +571,6 @@ macro_rules! impl_tick_filter { Self { table_ticks: None, entities: None, - entity_table_rows: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) .then(|| world.storages().sparse_sets.get(state.component_id).unwrap()), marker: PhantomData, @@ -597,7 +595,6 @@ macro_rules! impl_tick_filter { unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.entity_table_rows = Some(archetype.entity_table_rows().into()); let table = &tables[archetype.table_id()]; self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); } @@ -652,7 +649,6 @@ macro_rules! impl_tick_filter { fn clone(&self) -> Self { Self { table_ticks: self.table_ticks.clone(), - entity_table_rows: self.entity_table_rows.clone(), marker: self.marker.clone(), entities: self.entities.clone(), sparse_set: self.sparse_set.clone(), From 729d17466fe1559589d955e80cbcd8b723069c96 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 16:59:58 -0700 Subject: [PATCH 05/42] Remove unneeded entities reference --- crates/bevy_ecs/src/query/fetch.rs | 154 ++++++++++++++-------------- crates/bevy_ecs/src/query/filter.rs | 12 ++- crates/bevy_ecs/src/query/state.rs | 4 +- 3 files changed, 84 insertions(+), 86 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index c9d47fb0c0f16..502c53914d01d 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -565,7 +565,6 @@ pub struct ReadFetch<'w, T> { // T::Storage = TableStorage table_components: Option>>, // T::Storage = SparseStorage - entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, } @@ -573,7 +572,6 @@ impl Clone for ReadFetch<'_, T> { fn clone(&self) -> Self { Self { table_components: self.table_components, - entities: self.entities, sparse_set: self.sparse_set, } } @@ -609,7 +607,6 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { ) -> ReadFetch<'w, T> { ReadFetch { table_components: None, - entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -634,19 +631,24 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { .unwrap(); self.table_components = Some(column.get_data_slice().into()); } - StorageType::SparseSet => self.entities = Some(archetype.entities().into()), + StorageType::SparseSet => {} } } #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - self.table_components = Some( - table - .get_column(state.component_id) - .unwrap() - .get_data_slice() - .into(), - ); + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + self.table_components = Some( + table + .get_column(state.component_id) + .unwrap() + .get_data_slice() + .into(), + ); + } + StorageType::SparseSet => {} + } } #[inline(always)] @@ -658,16 +660,12 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { .unwrap_or_else(|| debug_checked_unreachable()); components.get(*table_row).deref() } - StorageType::SparseSet => { - let (_, sparse_set) = self - .entities - .zip(self.sparse_set) - .unwrap_or_else(|| debug_checked_unreachable()); - sparse_set - .get(*entity) - .unwrap_or_else(|| debug_checked_unreachable()) - .deref::() - } + StorageType::SparseSet => self + .sparse_set + .unwrap_or_else(|| debug_checked_unreachable()) + .get(*entity) + .unwrap_or_else(|| debug_checked_unreachable()) + .deref::(), } } } @@ -687,7 +685,6 @@ pub struct WriteFetch<'w, T> { table_components: Option>>, table_ticks: Option>>, // T::Storage = SparseStorage - entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, last_change_tick: u32, @@ -699,7 +696,6 @@ impl Clone for WriteFetch<'_, T> { Self { table_components: self.table_components, table_ticks: self.table_ticks, - entities: self.entities, sparse_set: self.sparse_set, last_change_tick: self.last_change_tick, change_tick: self.change_tick, @@ -712,7 +708,6 @@ pub struct ReadOnlyWriteFetch<'w, T> { // T::Storage = TableStorage table_components: Option>>, // T::Storage = SparseStorage - entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, } @@ -723,7 +718,6 @@ impl Clone for ReadOnlyWriteFetch<'_, T> { fn clone(&self) -> Self { Self { table_components: self.table_components, - entities: self.entities, sparse_set: self.sparse_set, } } @@ -804,7 +798,6 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { ) -> Self { Self { table_components: None, - entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -833,15 +826,20 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { self.table_components = Some(column.get_data_slice().into()); self.table_ticks = Some(column.get_ticks_slice().into()); } - StorageType::SparseSet => self.entities = Some(archetype.entities().into()), + StorageType::SparseSet => {} } } #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - let column = table.get_column(state.component_id).unwrap(); - self.table_components = Some(column.get_data_slice().into()); - self.table_ticks = Some(column.get_ticks_slice().into()); + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + let column = table.get_column(state.component_id).unwrap(); + self.table_components = Some(column.get_data_slice().into()); + self.table_ticks = Some(column.get_ticks_slice().into()); + } + StorageType::SparseSet => {} + } } #[inline(always)] @@ -863,11 +861,9 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { } } StorageType::SparseSet => { - let (_, sparse_set) = self - .entities - .zip(self.sparse_set) - .unwrap_or_else(|| debug_checked_unreachable()); - let (component, component_ticks) = sparse_set + let (component, component_ticks) = self + .sparse_set + .unwrap_or_else(|| debug_checked_unreachable()) .get_with_ticks(*entity) .unwrap_or_else(|| debug_checked_unreachable()); Mut { @@ -904,7 +900,6 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { ) -> Self { Self { table_components: None, - entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -929,19 +924,24 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { .unwrap(); self.table_components = Some(column.get_data_slice().into()); } - StorageType::SparseSet => self.entities = Some(archetype.entities().into()), + StorageType::SparseSet => {} } } #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - self.table_components = Some( - table - .get_column(state.component_id) - .unwrap() - .get_data_slice() - .into(), - ); + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + self.table_components = Some( + table + .get_column(state.component_id) + .unwrap() + .get_data_slice() + .into(), + ); + } + StorageType::SparseSet => {} + } } #[inline(always)] @@ -953,16 +953,12 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { .unwrap_or_else(|| debug_checked_unreachable()); components.get(*table_row).deref() } - StorageType::SparseSet => { - let (_, sparse_set) = self - .entities - .zip(self.sparse_set) - .unwrap_or_else(|| debug_checked_unreachable()); - sparse_set - .get(*entity) - .unwrap_or_else(|| debug_checked_unreachable()) - .deref::() - } + StorageType::SparseSet => self + .sparse_set + .unwrap_or_else(|| debug_checked_unreachable()) + .get(*entity) + .unwrap_or_else(|| debug_checked_unreachable()) + .deref::(), } } } @@ -1210,7 +1206,6 @@ pub struct ChangeTrackersFetch<'w, T> { // T::Storage = TableStorage table_ticks: Option>>, // T::Storage = SparseStorage - entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, marker: PhantomData, @@ -1222,7 +1217,6 @@ impl Clone for ChangeTrackersFetch<'_, T> { fn clone(&self) -> Self { Self { table_ticks: self.table_ticks, - entities: self.entities, sparse_set: self.sparse_set, marker: self.marker, last_change_tick: self.last_change_tick, @@ -1261,7 +1255,6 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { ) -> ChangeTrackersFetch<'w, T> { ChangeTrackersFetch { table_ticks: None, - entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -1289,19 +1282,24 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { .unwrap(); self.table_ticks = Some(column.get_ticks_slice().into()); } - StorageType::SparseSet => self.entities = Some(archetype.entities().into()), + StorageType::SparseSet => {} } } #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - self.table_ticks = Some( - table - .get_column(state.component_id) - .unwrap() - .get_ticks_slice() - .into(), - ); + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + self.table_ticks = Some( + table + .get_column(state.component_id) + .unwrap() + .get_ticks_slice() + .into(), + ); + } + StorageType::SparseSet => {} + } } #[inline(always)] @@ -1318,20 +1316,18 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { last_change_tick: self.last_change_tick, change_tick: self.change_tick, }, - StorageType::SparseSet => { - ChangeTrackers { - component_ticks: self - .sparse_set - .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(*entity) - .map(|ticks| &*ticks.get()) - .cloned() - .unwrap_or_else(|| debug_checked_unreachable()), - marker: PhantomData, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - } - } + StorageType::SparseSet => ChangeTrackers { + component_ticks: self + .sparse_set + .unwrap_or_else(|| debug_checked_unreachable()) + .get_ticks(*entity) + .map(|ticks| &*ticks.get()) + .cloned() + .unwrap_or_else(|| debug_checked_unreachable()), + marker: PhantomData, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, } } } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 0f43bf2923c95..7b206bd588b7c 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -498,7 +498,6 @@ macro_rules! impl_tick_filter { pub struct $fetch_name<'w, T> { table_ticks: Option>>, marker: PhantomData, - entities: Option>, sparse_set: Option<&'w ComponentSparseSet>, last_change_tick: u32, change_tick: u32, @@ -570,7 +569,6 @@ macro_rules! impl_tick_filter { unsafe fn init(world: &'w World, state: & $state_name, last_change_tick: u32, change_tick: u32) -> Self { Self { table_ticks: None, - entities: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) .then(|| world.storages().sparse_sets.get(state.component_id).unwrap()), marker: PhantomData, @@ -589,7 +587,12 @@ macro_rules! impl_tick_filter { const IS_ARCHETYPAL: bool = false; unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); + match T::Storage::STORAGE_TYPE { + StorageType::Table => { + self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); + } + StorageType::SparseSet => {}, + } } unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) { @@ -598,7 +601,7 @@ macro_rules! impl_tick_filter { let table = &tables[archetype.table_id()]; self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); } - StorageType::SparseSet => self.entities = Some(archetype.entities().into()), + StorageType::SparseSet => {}, } } @@ -650,7 +653,6 @@ macro_rules! impl_tick_filter { Self { table_ticks: self.table_ticks.clone(), marker: self.marker.clone(), - entities: self.entities.clone(), sparse_set: self.sparse_set.clone(), last_change_tick: self.last_change_tick.clone(), change_tick: self.change_tick.clone(), diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index db327288ec26c..670e2db549a17 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -818,7 +818,7 @@ impl QueryState { let rows = archetype.entity_table_rows(); let entities = archetype.entities(); - for idx in 0..archetype.len() { + for idx in 0..archetype.len() { let row = rows.get_unchecked(idx); let entity = entities.get_unchecked(idx); if !filter.filter_fetch(entity, row) { @@ -923,7 +923,7 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - for idx in offset..offset + len { + for idx in offset..offset + len { let row = rows.get_unchecked(idx); let entity = entities.get_unchecked(idx); if !filter.filter_fetch(entity, row) { From a925fc34bcfe4f091e8d81cae2d4e1ababc92911 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 17:19:41 -0700 Subject: [PATCH 06/42] Fix WorldQuery derive macro --- crates/bevy_ecs/macros/src/fetch.rs | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs index f7d0433306e5b..c7697b8c85471 100644 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ b/crates/bevy_ecs/macros/src/fetch.rs @@ -219,34 +219,19 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { #(self.#field_idents.set_table(&_state.#field_idents, _table);)* } - /// SAFETY: we call `table_fetch` for each member that implements `Fetch`. - #[inline] - unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item { - Self::Item { - #(#field_idents: self.#field_idents.table_fetch(_table_row),)* - #(#ignored_field_idents: Default::default(),)* - } - } - - /// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`. - #[inline] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item { + /// SAFETY: we call `fetch` for each member that implements `Fetch`. + #[inline(always)] + unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { Self::Item { - #(#field_idents: self.#field_idents.archetype_fetch(_archetype_index),)* + #(#field_idents: self.#field_idents.fetch(_entity, _table_row),)* #(#ignored_field_idents: Default::default(),)* } } #[allow(unused_variables)] - #[inline] - unsafe fn table_filter_fetch(&mut self, _table_row: usize) -> bool { - true #(&& self.#field_idents.table_filter_fetch(_table_row))* - } - - #[allow(unused_variables)] - #[inline] - unsafe fn archetype_filter_fetch(&mut self, _archetype_index: usize) -> bool { - true #(&& self.#field_idents.archetype_filter_fetch(_archetype_index))* + #[inline(always)] + unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> bool { + true #(&& self.#field_idents.filter_fetch(_entity, _table_row))* } } } From ed0c1a33c354532ea41d4a4725702d094a46f5ef Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 19:18:08 -0700 Subject: [PATCH 07/42] Add missing safety docs --- crates/bevy_ecs/src/query/fetch.rs | 15 ++++++++++++++- crates/bevy_ecs/src/query/filter.rs | 2 +- crates/bevy_ecs/src/query/iter.rs | 3 ++- crates/bevy_ecs/src/query/state.rs | 4 ++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 502c53914d01d..82842e8489cad 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -374,8 +374,21 @@ pub trait Fetch<'world>: Sized { /// [`Self::State`] this was initialized with. unsafe fn set_table(&mut self, state: &Self::State, table: &'world Table); + /// Fetch [`Self::Item`] for either the given `entity` in the current [`Table`], or for the given + /// `entity` in the current [`Archetype`]. This must always be called after [`Fetch::set_table`] + /// with a `table_row` in the range of the current [`Table`] or after [`Fetch::set_archetype`] + /// with a `entity` in the current archetype. + /// + /// # Safety + /// + /// Must always be called _after_ [`Fetch::set_table`] or [`Fetch::set_archetype`]. `entity` and + /// `table_row` must be in the range of the current table and archetype. unsafe fn fetch(&mut self, entity: &Entity, table_index: &usize) -> Self::Item; + /// # Safety + /// + /// Must always be called _after_ [`Fetch::set_table`] or [`Fetch::set_archetype`]. `entity` and + /// `table_row` must be in the range of the current table and archetype. #[allow(unused_variables)] #[inline(always)] unsafe fn filter_fetch(&mut self, entity: &Entity, table_index: &usize) -> bool { @@ -1353,7 +1366,7 @@ macro_rules! impl_tuple_fetch { ($($name::init(_world, $name, _last_change_tick, _change_tick),)*) } - const IS_DENSE: bool = true $(|| $name::IS_DENSE)*; + const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 7b206bd588b7c..304ce02c0329e 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -366,7 +366,7 @@ macro_rules! impl_query_filter_tuple { type State = Or<($(<$filter as Fetch<'w>>::State,)*)>; type Item = bool; - const IS_DENSE: bool = true $(|| $filter::IS_DENSE)*; + const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*; diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 08459972daf44..e15fc18f6e540 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -288,7 +288,7 @@ impl<'w, 's, Q: WorldQuery, QF, F: WorldQuery> QueryIterationCursor<'w, 's, Q, Q where QF: Fetch<'w, State = Q::State>, { - const IS_DENSE: bool = QF::IS_DENSE || >::IS_DENSE; + const IS_DENSE: bool = QF::IS_DENSE && >::IS_DENSE; unsafe fn init_empty( world: &'w World, @@ -375,6 +375,7 @@ where continue; } + println!("{} {} {:?}", self.current_index, self.current_len, self.entities); let entity = self.entities.unwrap().get_unchecked(self.current_index); if !self.filter.filter_fetch(entity, &self.current_index) { self.current_index += 1; diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 670e2db549a17..4d73a70d88277 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -792,7 +792,7 @@ impl QueryState { change_tick, ); - if >::IS_DENSE || >::IS_DENSE { + if >::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; @@ -856,7 +856,7 @@ impl QueryState { // NOTE: If you are changing query iteration code, remember to update the following places, where relevant: // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual task_pool.scope(|scope| { - if QF::IS_DENSE || >::IS_DENSE { + if QF::IS_DENSE && >::IS_DENSE { let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; From 6cfcbcc8d118c326000de3a1f0f32fe47eb57ba6 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 18 May 2022 22:32:02 -0700 Subject: [PATCH 08/42] CI fixes and debug_unwrap_unchecked --- crates/bevy_ecs/src/query/fetch.rs | 9 ++++----- crates/bevy_ecs/src/query/iter.rs | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 82842e8489cad..20b6ad09c4255 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -339,9 +339,9 @@ pub trait Fetch<'world>: Sized { /// Returns true if (and only if) every table of every archetype matched by this fetch contains /// all of the matched components. This is used to select a more efficient "table iterator" - /// for "dense" queries. If this returns true, [`Fetch::set_table`] and [`Fetch::table_fetch`] - /// will be called for iterators. If this returns false, [`Fetch::set_archetype`] and - /// [`Fetch::archetype_fetch`] will be called for iterators. + /// for "dense" queries. If this returns true, [`Fetch::set_table`] before [`Fetch::fetch`] + /// will be called for iterators. If this returns false, [`Fetch::set_archetype`] will be used + /// before [`Fetch::fetch`] will be called for iterators. const IS_DENSE: bool; /// Returns true if (and only if) this Fetch relies strictly on archetypes to limit which @@ -404,8 +404,7 @@ pub trait Fetch<'world>: Sized { /// /// Implementor must ensure that [`FetchState::update_component_access`] and /// [`FetchState::update_archetype_component_access`] exactly reflects the results of -/// [`FetchState::matches_archetype`], [`FetchState::matches_table`], [`Fetch::archetype_fetch`], and -/// [`Fetch::table_fetch`]. +/// [`FetchState::matches_archetype`], [`FetchState::matches_table`], and [`Fetch::fetch`]. pub unsafe trait FetchState: Send + Sync + Sized { fn init(world: &mut World) -> Self; fn update_component_access(&self, access: &mut FilteredAccess); diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index e15fc18f6e540..106e48b925666 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{ArchetypeId, Archetypes}, entity::Entity, - query::{Fetch, QueryState, WorldQuery}, + query::{debug_checked_unreachable, Fetch, QueryState, WorldQuery}, storage::{TableId, Tables}, world::World, }; @@ -273,8 +273,8 @@ where Self { table_id_iter: self.table_id_iter.clone(), archetype_id_iter: self.archetype_id_iter.clone(), - entities: None, - rows: None, + entities: self.entities, + rows: self.rows, fetch: self.fetch.clone(), filter: self.filter.clone(), current_len: self.current_len, @@ -375,8 +375,10 @@ where continue; } - println!("{} {} {:?}", self.current_index, self.current_len, self.entities); - let entity = self.entities.unwrap().get_unchecked(self.current_index); + let entity = self + .entities + .unwrap_or_else(|| debug_checked_unreachable()) + .get_unchecked(self.current_index); if !self.filter.filter_fetch(entity, &self.current_index) { self.current_index += 1; continue; @@ -404,8 +406,14 @@ where continue; } - let entity = self.entities.unwrap().get_unchecked(self.current_index); - let row = self.rows.unwrap().get_unchecked(self.current_index); + let entity = self + .entities + .unwrap_or_else(|| debug_checked_unreachable()) + .get_unchecked(self.current_index); + let row = self + .rows + .unwrap_or_else(|| debug_checked_unreachable()) + .get_unchecked(self.current_index); if !self.filter.filter_fetch(entity, row) { self.current_index += 1; continue; From cac2927304f1af1800033129ecbe96f9cd7be2eb Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 19 May 2022 22:29:37 -0700 Subject: [PATCH 09/42] Coallese entity and row together in Archetype --- crates/bevy_ecs/src/archetype.rs | 51 +++++++++++++------------- crates/bevy_ecs/src/lib.rs | 2 +- crates/bevy_ecs/src/query/fetch.rs | 27 +++++++------- crates/bevy_ecs/src/query/filter.rs | 37 ++++++++++++++++--- crates/bevy_ecs/src/query/iter.rs | 51 ++++++++++++-------------- crates/bevy_ecs/src/query/state.rs | 27 ++++++++------ crates/bevy_scene/src/dynamic_scene.rs | 5 ++- crates/bevy_scene/src/scene_spawner.rs | 4 +- 8 files changed, 114 insertions(+), 90 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index ab33119a93be6..abd97b5a4769c 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -100,9 +100,19 @@ impl Edges { } } -struct TableInfo { - id: TableId, - entity_rows: Vec, +pub struct ArchetypeEntity { + pub(crate) entity: Entity, + pub(crate) table_row: usize, +} + +impl ArchetypeEntity { + pub fn entity(&self) -> Entity { + self.entity + } + + pub fn table_row(&self) -> usize { + self.table_row + } } pub(crate) struct ArchetypeSwapRemoveResult { @@ -117,9 +127,9 @@ pub(crate) struct ArchetypeComponentInfo { pub struct Archetype { id: ArchetypeId, - entities: Vec, + table_id: TableId, edges: Edges, - table_info: TableInfo, + entities: Vec, table_components: Box<[ComponentId]>, sparse_set_components: Box<[ComponentId]>, pub(crate) unique_components: SparseSet, @@ -163,15 +173,12 @@ impl Archetype { } Self { id, - table_info: TableInfo { - id: table_id, - entity_rows: Default::default(), - }, + table_id, + entities: Vec::new(), components, table_components, sparse_set_components, unique_components: SparseSet::new(), - entities: Default::default(), edges: Default::default(), } } @@ -183,19 +190,14 @@ impl Archetype { #[inline] pub fn table_id(&self) -> TableId { - self.table_info.id + self.table_id } #[inline] - pub fn entities(&self) -> &[Entity] { + pub fn entities(&self) -> &[ArchetypeEntity] { &self.entities } - #[inline] - pub fn entity_table_rows(&self) -> &[usize] { - &self.table_info.entity_rows - } - #[inline] pub fn table_components(&self) -> &[ComponentId] { &self.table_components @@ -233,20 +235,19 @@ impl Archetype { #[inline] pub fn entity_table_row(&self, index: usize) -> usize { - self.table_info.entity_rows[index] + self.entities[index].table_row } #[inline] pub fn set_entity_table_row(&mut self, index: usize, table_row: usize) { - self.table_info.entity_rows[index] = table_row; + self.entities[index].table_row = table_row; } /// # Safety /// valid component values must be immediately written to the relevant storages /// `table_row` must be valid pub unsafe fn allocate(&mut self, entity: Entity, table_row: usize) -> EntityLocation { - self.entities.push(entity); - self.table_info.entity_rows.push(table_row); + self.entities.push(ArchetypeEntity { entity, table_row }); EntityLocation { archetype_id: self.id, @@ -256,21 +257,20 @@ impl Archetype { pub fn reserve(&mut self, additional: usize) { self.entities.reserve(additional); - self.table_info.entity_rows.reserve(additional); } /// Removes the entity at `index` by swapping it out. Returns the table row the entity is stored /// in. pub(crate) fn swap_remove(&mut self, index: usize) -> ArchetypeSwapRemoveResult { let is_last = index == self.entities.len() - 1; - self.entities.swap_remove(index); + let entity = self.entities.swap_remove(index); ArchetypeSwapRemoveResult { swapped_entity: if is_last { None } else { - Some(self.entities[index]) + Some(self.entities[index].entity) }, - table_row: self.table_info.entity_rows.swap_remove(index), + table_row: entity.table_row, } } @@ -308,7 +308,6 @@ impl Archetype { pub(crate) fn clear_entities(&mut self) { self.entities.clear(); - self.table_info.entity_rows.clear(); } } diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 5f1bdc3d0b113..c09d20c56bf39 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -542,7 +542,7 @@ mod tests { .spawn() .insert_bundle((TableStored("def"), A(456), SparseStored(1))) .id(); - // // this should be skipped + // this should be skipped // SparseStored(1).spawn().insert("abc"); let ents = world .query::<(Entity, Option<&SparseStored>, &A)>() diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 20b6ad09c4255..c353d28f95ba5 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -624,7 +624,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { .storages() .sparse_sets .get(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) }), } } @@ -654,7 +654,7 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { self.table_components = Some( table .get_column(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) .get_data_slice() .into(), ); @@ -666,18 +666,17 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { #[inline(always)] unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let components = self - .table_components - .unwrap_or_else(|| debug_checked_unreachable()); - components.get(*table_row).deref() - } + StorageType::Table => self + .table_components + .unwrap_or_else(|| debug_checked_unreachable()) + .get(*table_row) + .deref(), StorageType::SparseSet => self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) .get(*entity) .unwrap_or_else(|| debug_checked_unreachable()) - .deref::(), + .deref(), } } } @@ -815,7 +814,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { .storages() .sparse_sets .get(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) }), table_ticks: None, last_change_tick, @@ -917,7 +916,7 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { .storages() .sparse_sets .get(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) }), } } @@ -947,7 +946,7 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { self.table_components = Some( table .get_column(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) .get_data_slice() .into(), ); @@ -970,7 +969,7 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { .unwrap_or_else(|| debug_checked_unreachable()) .get(*entity) .unwrap_or_else(|| debug_checked_unreachable()) - .deref::(), + .deref(), } } } @@ -1272,7 +1271,7 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { .storages() .sparse_sets .get(state.component_id) - .unwrap() + .unwrap_or_else(|| debug_checked_unreachable()) }), marker: PhantomData, last_change_tick, diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 304ce02c0329e..123cf10565b16 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -570,7 +570,12 @@ macro_rules! impl_tick_filter { Self { table_ticks: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet) - .then(|| world.storages().sparse_sets.get(state.component_id).unwrap()), + .then(|| { + world.storages() + .sparse_sets + .get(state.component_id) + .unwrap_or_else(|| debug_checked_unreachable()) + }), marker: PhantomData, last_change_tick, change_tick, @@ -589,7 +594,12 @@ macro_rules! impl_tick_filter { unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { match T::Storage::STORAGE_TYPE { StorageType::Table => { - self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); + self.table_ticks = Some( + table.get_column(state.component_id) + .unwrap_or_else(|| debug_checked_unreachable()) + .get_ticks_slice() + .into() + ); } StorageType::SparseSet => {}, } @@ -609,7 +619,14 @@ macro_rules! impl_tick_filter { unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(*table_row)).deref(), self.last_change_tick, self.change_tick) + $is_detected(&*( + self.table_ticks + .unwrap_or_else(|| debug_checked_unreachable()) + .get(*table_row)) + .deref(), + self.last_change_tick, + self.change_tick + ) } StorageType::SparseSet => { let ticks = self @@ -618,7 +635,7 @@ macro_rules! impl_tick_filter { .get_ticks(*entity) .map(|ticks| &*ticks.get()) .cloned() - .unwrap(); + .unwrap_or_else(|| debug_checked_unreachable()); $is_detected(&ticks, self.last_change_tick, self.change_tick) } } @@ -629,7 +646,15 @@ macro_rules! impl_tick_filter { // Explicitly repeated code here to avoid potential poor inlining match T::Storage::STORAGE_TYPE { StorageType::Table => { - $is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(*_table_row)).deref(), self.last_change_tick, self.change_tick) + $is_detected(&*( + self + .table_ticks + .unwrap_or_else(|| debug_checked_unreachable()) + .get(*_table_row)) + .deref(), + self.last_change_tick, + self.change_tick + ) } StorageType::SparseSet => { let ticks = self @@ -638,7 +663,7 @@ macro_rules! impl_tick_filter { .get_ticks(*_entity) .map(|ticks| &*ticks.get()) .cloned() - .unwrap(); + .unwrap_or_else(|| debug_checked_unreachable()); $is_detected(&ticks, self.last_change_tick, self.change_tick) } } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 106e48b925666..f9e1766539387 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -1,7 +1,7 @@ use crate::{ - archetype::{ArchetypeId, Archetypes}, + archetype::{ArchetypeEntity, ArchetypeId, Archetypes}, entity::Entity, - query::{debug_checked_unreachable, Fetch, QueryState, WorldQuery}, + query::{Fetch, QueryState, WorldQuery}, storage::{TableId, Tables}, world::World, }; @@ -255,8 +255,8 @@ where struct QueryIterationCursor<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, - entities: Option<&'s [Entity]>, - rows: Option<&'s [usize]>, + entities: &'s [Entity], + archetype_entities: &'s [ArchetypeEntity], fetch: QF, filter: QueryFetch<'w, F>, current_len: usize, @@ -274,7 +274,7 @@ where table_id_iter: self.table_id_iter.clone(), archetype_id_iter: self.archetype_id_iter.clone(), entities: self.entities, - rows: self.rows, + archetype_entities: self.archetype_entities, fetch: self.fetch.clone(), filter: self.filter.clone(), current_len: self.current_len, @@ -324,8 +324,8 @@ where QueryIterationCursor { fetch, filter, - entities: None, - rows: None, + entities: &[], + archetype_entities: &[], table_id_iter: query_state.matched_table_ids.iter(), archetype_id_iter: query_state.matched_archetype_ids.iter(), current_len: 0, @@ -339,12 +339,15 @@ where unsafe fn peek_last(&mut self) -> Option { if self.current_index > 0 { let index = self.current_index - 1; - let entity = self.entities.unwrap().get_unchecked(index); if Self::IS_DENSE { + let entity = self.entities.get_unchecked(index); Some(self.fetch.fetch(entity, &index)) } else { - let row = self.rows.unwrap().get_unchecked(index); - Some(self.fetch.fetch(entity, row)) + let archetype_entity = self.archetype_entities.get_unchecked(index); + Some( + self.fetch + .fetch(&archetype_entity.entity, &archetype_entity.table_row), + ) } } else { None @@ -369,16 +372,13 @@ where self.filter.set_table(&query_state.filter_state, table); // This borrow is valid for the lifetime of the state, but the compiler // can't prove that. - self.entities = Some(std::mem::transmute(table.entities())); + self.entities = std::mem::transmute(table.entities()); self.current_len = table.len(); self.current_index = 0; continue; } - let entity = self - .entities - .unwrap_or_else(|| debug_checked_unreachable()) - .get_unchecked(self.current_index); + let entity = self.entities.get_unchecked(self.current_index); if !self.filter.filter_fetch(entity, &self.current_index) { self.current_index += 1; continue; @@ -399,27 +399,24 @@ where .set_archetype(&query_state.filter_state, archetype, tables); // These borrows are valid for the lifetime of the state, but the compiler // can't prove that. - self.entities = Some(std::mem::transmute(archetype.entities())); - self.rows = Some(std::mem::transmute(archetype.entity_table_rows())); + self.archetype_entities = std::mem::transmute(archetype.entities()); self.current_len = archetype.len(); self.current_index = 0; continue; } - let entity = self - .entities - .unwrap_or_else(|| debug_checked_unreachable()) - .get_unchecked(self.current_index); - let row = self - .rows - .unwrap_or_else(|| debug_checked_unreachable()) - .get_unchecked(self.current_index); - if !self.filter.filter_fetch(entity, row) { + let archetype_entity = self.archetype_entities.get_unchecked(self.current_index); + if !self + .filter + .filter_fetch(&archetype_entity.entity, &archetype_entity.table_row) + { self.current_index += 1; continue; } - let item = self.fetch.fetch(entity, row); + let item = self + .fetch + .fetch(&archetype_entity.entity, &archetype_entity.table_row); self.current_index += 1; return Some(item); } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 4d73a70d88277..1befee2ecabae 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -792,8 +792,8 @@ impl QueryState { change_tick, ); + let tables = &world.storages().tables; if >::IS_DENSE && >::IS_DENSE { - let tables = &world.storages().tables; for table_id in &self.matched_table_ids { let table = &tables[*table_id]; fetch.set_table(&self.fetch_state, table); @@ -810,21 +810,18 @@ impl QueryState { } } else { let archetypes = &world.archetypes; - let tables = &world.storages().tables; for archetype_id in &self.matched_archetype_ids { let archetype = &archetypes[*archetype_id]; fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - let rows = archetype.entity_table_rows(); let entities = archetype.entities(); for idx in 0..archetype.len() { - let row = rows.get_unchecked(idx); - let entity = entities.get_unchecked(idx); - if !filter.filter_fetch(entity, row) { + let archetype_entity = entities.get_unchecked(idx); + if !filter.filter_fetch(&archetype_entity.entity, &archetype_entity.table_row) { continue; } - func(fetch.fetch(entity, row)); + func(fetch.fetch(&archetype_entity.entity, &archetype_entity.table_row)); } } } @@ -918,18 +915,24 @@ impl QueryState { ); let tables = &world.storages().tables; let archetype = &world.archetypes[*archetype_id]; - let rows = archetype.entity_table_rows(); let entities = archetype.entities(); fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); for idx in offset..offset + len { - let row = rows.get_unchecked(idx); - let entity = entities.get_unchecked(idx); - if !filter.filter_fetch(entity, row) { + let archetype_entity = entities.get_unchecked(idx); + if !filter.filter_fetch( + &archetype_entity.entity, + &archetype_entity.table_row, + ) { continue; } - func(fetch.fetch(entity, row)); + func( + fetch.fetch( + &archetype_entity.entity, + &archetype_entity.table_row, + ), + ); } }; diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 3f2402850f548..c3849bcb8cc5a 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -42,7 +42,7 @@ impl DynamicScene { // and insert it into the dynamic scene. for entity in archetype.entities() { scene.entities.push(DynamicEntity { - entity: entity.id(), + entity: entity.entity().id(), components: Vec::new(), }); } @@ -56,7 +56,8 @@ impl DynamicScene { .and_then(|registration| registration.data::()); if let Some(reflect_component) = reflect_component { for (i, entity) in archetype.entities().iter().enumerate() { - if let Some(component) = reflect_component.reflect_component(world, *entity) + if let Some(component) = + reflect_component.reflect_component(world, entity.entity()) { scene.entities[entities_offset + i] .components diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 142d299743efd..b1ffed73a9c71 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -158,7 +158,7 @@ impl SceneSpawner { for scene_entity in archetype.entities() { let entity = *instance_info .entity_map - .entry(*scene_entity) + .entry(scene_entity.entity()) .or_insert_with(|| world.spawn().id()); for component_id in archetype.components() { let component_info = scene @@ -182,7 +182,7 @@ impl SceneSpawner { reflect_component.copy_component( &scene.world, world, - *scene_entity, + scene_entity.entity(), entity, ); } From 389477f4ce27ecaceceb9f5e9df515c9433f2fc2 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 14:55:35 -0700 Subject: [PATCH 10/42] Fix build from bad merge --- crates/bevy_ecs/src/query/state.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 91d3542b10111..aed639ecbd2d2 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -896,13 +896,15 @@ impl QueryState { ); let tables = &world.storages().tables; let table = &tables[*table_id]; + let entities = table.entities(); fetch.set_table(&self.fetch_state, table); filter.set_table(&self.filter_state, table); - for table_index in offset..offset + len { - if !filter.table_filter_fetch(table_index) { + for row in offset..offset + len { + let entity = entities.get_unchecked(row); + if !filter.filter_fetch(entity, &row) { continue; } - let item = fetch.table_fetch(table_index); + let item = fetch.fetch(entity, &row); func(item); } }; @@ -945,11 +947,19 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); + let entities = archetype.entities(); for archetype_index in offset..offset + len { - if !filter.archetype_filter_fetch(archetype_index) { + let archetype_entity = entities.get_unchecked(archetype_index); + if !filter.filter_fetch( + &archetype_entity.entity, + &archetype_entity.table_row, + ) { continue; } - func(fetch.archetype_fetch(archetype_index)); + func(fetch.fetch( + &archetype_entity.entity, + &archetype_entity.table_row, + )); } }; From 95f57d73e44e45b3de508c71f5d2fd66d19052f4 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 09:45:21 -0700 Subject: [PATCH 11/42] Remove transmutes --- crates/bevy_ecs/src/query/iter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 612467397809e..bcfeb7c219658 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -255,8 +255,8 @@ where struct QueryIterationCursor<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::State>, F: WorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, - entities: &'s [Entity], - archetype_entities: &'s [ArchetypeEntity], + entities: &'w [Entity], + archetype_entities: &'w [ArchetypeEntity], fetch: QF, filter: QueryFetch<'w, F>, current_len: usize, @@ -372,7 +372,7 @@ where self.filter.set_table(&query_state.filter_state, table); // This borrow is valid for the lifetime of the state, but the compiler // can't prove that. - self.entities = std::mem::transmute(table.entities()); + self.entities = table.entities(); self.current_len = table.len(); self.current_index = 0; continue; @@ -399,7 +399,7 @@ where .set_archetype(&query_state.filter_state, archetype, tables); // These borrows are valid for the lifetime of the state, but the compiler // can't prove that. - self.archetype_entities = std::mem::transmute(archetype.entities()); + self.archetype_entities = archetype.entities(); self.current_len = archetype.len(); self.current_index = 0; continue; From 8fef6c8121e67783fc09dda8cf950e7f5cdd9beb Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 09:49:39 -0700 Subject: [PATCH 12/42] Remove unnecessary lifetime --- crates/bevy_ecs/src/query/fetch.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index de09a1216b7fd..00eb48e0c89da 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -434,12 +434,10 @@ impl WorldQuery for Entity { /// The [`Fetch`] of [`Entity`]. #[doc(hidden)] #[derive(Clone)] -pub struct EntityFetch<'w> { - marker: PhantomData<&'w Entity>, -} +pub struct EntityFetch; /// SAFETY: access is read only -unsafe impl<'w> ReadOnlyFetch for EntityFetch<'w> {} +unsafe impl<'w> ReadOnlyFetch for EntityFetch {} /// The [`FetchState`] of [`Entity`]. #[doc(hidden)] @@ -467,12 +465,12 @@ unsafe impl FetchState for EntityState { } impl<'w> WorldQueryGats<'w> for Entity { - type Fetch = EntityFetch<'w>; - type ReadOnlyFetch = EntityFetch<'w>; + type Fetch = EntityFetch; + type ReadOnlyFetch = EntityFetch; type _State = EntityState; } -impl<'w> Fetch<'w> for EntityFetch<'w> { +impl<'w> Fetch<'w> for EntityFetch { type Item = Entity; type State = EntityState; @@ -485,10 +483,8 @@ impl<'w> Fetch<'w> for EntityFetch<'w> { _state: &EntityState, _last_change_tick: u32, _change_tick: u32, - ) -> EntityFetch<'w> { - EntityFetch { - marker: PhantomData, - } + ) -> EntityFetch { + EntityFetch } #[inline] From 91eda721dcc994caf0277bf4e6f3832147f6e8d4 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 09:59:54 -0700 Subject: [PATCH 13/42] Revert code duplication. --- crates/bevy_ecs/src/query/filter.rs | 33 ++++------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index b1b71249b95b8..49292a75f4f81 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -401,10 +401,8 @@ macro_rules! impl_query_filter_tuple { } #[inline(always)] - unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { - // Explicitly repeated code here to avoid potential poor inlining - let ($($filter,)*) = &mut self.0; - false $(|| ($filter.matches && $filter.fetch.filter_fetch(_entity, _table_row)))* + unsafe fn filter_fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + self.fetch(entity, table_row) } } @@ -624,31 +622,8 @@ macro_rules! impl_tick_filter { } #[inline(always)] - unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { - // Explicitly repeated code here to avoid potential poor inlining - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - $is_detected(&*( - self - .table_ticks - .unwrap_or_else(|| debug_checked_unreachable()) - .get(*_table_row)) - .deref(), - self.last_change_tick, - self.change_tick - ) - } - StorageType::SparseSet => { - let ticks = self - .sparse_set - .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(*_entity) - .map(|ticks| &*ticks.get()) - .cloned() - .unwrap_or_else(|| debug_checked_unreachable()); - $is_detected(&ticks, self.last_change_tick, self.change_tick) - } - } + unsafe fn filter_fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + self.fetch(entity, table_row) } } From 4ea8e34b7c33946f366faff7ea55f14176bfe783 Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 10:01:11 -0700 Subject: [PATCH 14/42] Fix AnyOf density --- crates/bevy_ecs/src/query/fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 00eb48e0c89da..e420f6e3d9357 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1448,7 +1448,7 @@ macro_rules! impl_anytuple_fetch { AnyOf(($(($name::init(_world, $name, _last_change_tick, _change_tick), false),)*)) } - const IS_DENSE: bool = true $(|| $name::IS_DENSE)*; + const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; From 3fbe9ea7267f510075a5be7b590d171015ca1d3d Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 10:08:02 -0700 Subject: [PATCH 15/42] Merge table_components and table_ticks together --- crates/bevy_ecs/src/query/fetch.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index e420f6e3d9357..59635f1e73a10 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -679,8 +679,10 @@ impl WorldQuery for &mut T { #[doc(hidden)] pub struct WriteFetch<'w, T> { // T::Storage = TableStorage - table_components: Option>>, - table_ticks: Option>>, + table_data: Option<( + ThinSlicePtr<'w, UnsafeCell>, + ThinSlicePtr<'w, UnsafeCell>, + )>, // T::Storage = SparseStorage sparse_set: Option<&'w ComponentSparseSet>, @@ -691,8 +693,7 @@ pub struct WriteFetch<'w, T> { impl Clone for WriteFetch<'_, T> { fn clone(&self) -> Self { Self { - table_components: self.table_components, - table_ticks: self.table_ticks, + table_data: self.table_data, sparse_set: self.sparse_set, last_change_tick: self.last_change_tick, change_tick: self.change_tick, @@ -790,7 +791,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { change_tick: u32, ) -> Self { Self { - table_components: None, + table_data: None, sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| { world .storages() @@ -798,7 +799,6 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { .get(state.component_id) .unwrap_or_else(|| debug_checked_unreachable()) }), - table_ticks: None, last_change_tick, change_tick, } @@ -816,8 +816,10 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { let column = tables[archetype.table_id()] .get_column(state.component_id) .unwrap(); - self.table_components = Some(column.get_data_slice().into()); - self.table_ticks = Some(column.get_ticks_slice().into()); + self.table_data = Some(( + column.get_data_slice().into(), + column.get_ticks_slice().into(), + )); } StorageType::SparseSet => {} } @@ -828,8 +830,10 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { match T::Storage::STORAGE_TYPE { StorageType::Table => { let column = table.get_column(state.component_id).unwrap(); - self.table_components = Some(column.get_data_slice().into()); - self.table_ticks = Some(column.get_ticks_slice().into()); + self.table_data = Some(( + column.get_data_slice().into(), + column.get_ticks_slice().into(), + )); } StorageType::SparseSet => {} } @@ -841,8 +845,7 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { StorageType::Table => { let table_row = *table_row; let (table_components, table_ticks) = self - .table_components - .zip(self.table_ticks) + .table_data .unwrap_or_else(|| debug_checked_unreachable()); Mut { value: table_components.get(table_row).deref_mut(), From 637e98152fc148ec4c6c3c4421a17c070e028eec Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 10:24:28 -0700 Subject: [PATCH 16/42] Remove matches in set_table impls --- crates/bevy_ecs/src/query/fetch.rs | 72 +++++++++++------------------ crates/bevy_ecs/src/query/filter.rs | 17 +++---- 2 files changed, 32 insertions(+), 57 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 59635f1e73a10..ae3b31c8fcc04 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -635,18 +635,13 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - self.table_components = Some( - table - .get_column(state.component_id) - .unwrap_or_else(|| debug_checked_unreachable()) - .get_data_slice() - .into(), - ); - } - StorageType::SparseSet => {} - } + self.table_components = Some( + table + .get_column(state.component_id) + .unwrap_or_else(|| debug_checked_unreachable()) + .get_data_slice() + .into(), + ); } #[inline(always)] @@ -827,16 +822,11 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let column = table.get_column(state.component_id).unwrap(); - self.table_data = Some(( - column.get_data_slice().into(), - column.get_ticks_slice().into(), - )); - } - StorageType::SparseSet => {} - } + let column = table.get_column(state.component_id).unwrap(); + self.table_data = Some(( + column.get_data_slice().into(), + column.get_ticks_slice().into(), + )); } #[inline(always)] @@ -926,18 +916,13 @@ impl<'w, T: Component> Fetch<'w> for ReadOnlyWriteFetch<'w, T> { #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - self.table_components = Some( - table - .get_column(state.component_id) - .unwrap_or_else(|| debug_checked_unreachable()) - .get_data_slice() - .into(), - ); - } - StorageType::SparseSet => {} - } + self.table_components = Some( + table + .get_column(state.component_id) + .unwrap_or_else(|| debug_checked_unreachable()) + .get_data_slice() + .into(), + ); } #[inline(always)] @@ -1283,18 +1268,13 @@ impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - self.table_ticks = Some( - table - .get_column(state.component_id) - .unwrap() - .get_ticks_slice() - .into(), - ); - } - StorageType::SparseSet => {} - } + self.table_ticks = Some( + table + .get_column(state.component_id) + .unwrap() + .get_ticks_slice() + .into(), + ); } #[inline(always)] diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 49292a75f4f81..88dbaee339ad9 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -572,17 +572,12 @@ macro_rules! impl_tick_filter { const IS_ARCHETYPAL: bool = false; unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - self.table_ticks = Some( - table.get_column(state.component_id) - .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks_slice() - .into() - ); - } - StorageType::SparseSet => {}, - } + self.table_ticks = Some( + table.get_column(state.component_id) + .unwrap_or_else(|| debug_checked_unreachable()) + .get_ticks_slice() + .into() + ); } unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) { From 7b44be9f8cb4ca50fdcab98abb4d007467b1571e Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 31 May 2022 10:30:05 -0700 Subject: [PATCH 17/42] Remove outdated comments --- crates/bevy_ecs/src/query/iter.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index bcfeb7c219658..7c229c4f1156c 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -370,8 +370,6 @@ where let table = &tables[*table_id]; self.fetch.set_table(&query_state.fetch_state, table); self.filter.set_table(&query_state.filter_state, table); - // This borrow is valid for the lifetime of the state, but the compiler - // can't prove that. self.entities = table.entities(); self.current_len = table.len(); self.current_index = 0; @@ -397,8 +395,6 @@ where .set_archetype(&query_state.fetch_state, archetype, tables); self.filter .set_archetype(&query_state.filter_state, archetype, tables); - // These borrows are valid for the lifetime of the state, but the compiler - // can't prove that. self.archetype_entities = archetype.entities(); self.current_len = archetype.len(); self.current_index = 0; From c04ee45ded4e3784a9bc49fe3f26a914f7aefcb8 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 17 Jun 2022 23:26:59 -0700 Subject: [PATCH 18/42] Remove unsafe on FetchState --- crates/bevy_ecs/src/query/fetch.rs | 20 +++++++------------- crates/bevy_ecs/src/query/filter.rs | 8 ++++---- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 18c3db6fa7adb..5f40406e8da5d 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -451,13 +451,7 @@ pub unsafe trait Fetch<'world>: Sized { /// State used to construct a Fetch. This will be cached inside [`QueryState`](crate::query::QueryState), /// so it is best to move as much data / computation here as possible to reduce the cost of /// constructing Fetch. -/// -/// # Safety -/// -/// Implementor must ensure that [`FetchState::update_component_access`] and -/// [`FetchState::update_archetype_component_access`] exactly reflects the results of -/// [`FetchState::matches_component_set`], and [`Fetch::fetch`]. -pub unsafe trait FetchState: Send + Sync + Sized { +pub trait FetchState: Send + Sync + Sized { fn init(world: &mut World) -> Self; fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool; } @@ -484,7 +478,7 @@ unsafe impl ReadOnlyWorldQuery for Entity {} #[doc(hidden)] pub struct EntityState; -unsafe impl FetchState for EntityState { +impl FetchState for EntityState { fn init(_world: &mut World) -> Self { Self } @@ -562,7 +556,7 @@ pub struct ComponentIdState { marker: PhantomData, } -unsafe impl FetchState for ComponentIdState { +impl FetchState for ComponentIdState { fn init(world: &mut World) -> Self { let component_id = world.init_component::(); ComponentIdState { @@ -893,7 +887,7 @@ pub struct OptionState { state: T, } -unsafe impl FetchState for OptionState { +impl FetchState for OptionState { fn init(world: &mut World) -> Self { Self { state: T::init(world), @@ -1064,7 +1058,7 @@ pub struct ChangeTrackersState { marker: PhantomData, } -unsafe impl FetchState for ChangeTrackersState { +impl FetchState for ChangeTrackersState { fn init(world: &mut World) -> Self { let component_id = world.init_component::(); Self { @@ -1292,7 +1286,7 @@ macro_rules! impl_tuple_fetch { #[allow(non_snake_case)] #[allow(clippy::unused_unit)] - unsafe impl<$($name: FetchState),*> FetchState for ($($name,)*) { + impl<$($name: FetchState),*> FetchState for ($($name,)*) { fn init(_world: &mut World) -> Self { ($($name::init(_world),)*) } @@ -1434,7 +1428,7 @@ macro_rules! impl_anytuple_fetch { #[allow(non_snake_case)] #[allow(clippy::unused_unit)] - unsafe impl<$($name: FetchState),*> FetchState for AnyOf<($($name,)*)> { + impl<$($name: FetchState),*> FetchState for AnyOf<($($name,)*)> { fn init(_world: &mut World) -> Self { AnyOf(($($name::init(_world),)*)) } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 8640a9b069a37..fa44371c72fc9 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -70,7 +70,7 @@ pub struct WithState { marker: PhantomData, } -unsafe impl FetchState for WithState { +impl FetchState for WithState { fn init(world: &mut World) -> Self { let component_id = world.init_component::(); Self { @@ -208,7 +208,7 @@ pub struct WithoutState { marker: PhantomData, } -unsafe impl FetchState for WithoutState { +impl FetchState for WithoutState { fn init(world: &mut World) -> Self { let component_id = world.init_component::(); Self { @@ -452,7 +452,7 @@ macro_rules! impl_query_filter_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] - unsafe impl<$($filter: FetchState),*> FetchState for Or<($($filter,)*)> { + impl<$($filter: FetchState),*> FetchState for Or<($($filter,)*)> { fn init(world: &mut World) -> Self { Or(($($filter::init(world),)*)) } @@ -510,7 +510,7 @@ macro_rules! impl_tick_filter { } } - unsafe impl FetchState for $state_name { + impl FetchState for $state_name { fn init(world: &mut World) -> Self { Self { component_id: world.init_component::(), From ed72c4cbbeeda824f2fbe51d22251b28adc316aa Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 17 Jun 2022 23:37:46 -0700 Subject: [PATCH 19/42] Fix docs --- crates/bevy_ecs/src/query/fetch.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 5f40406e8da5d..dd2af4fef14f2 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -359,8 +359,7 @@ pub trait WorldQueryGats<'world> { /// /// Implementor must ensure that [`Fetch::update_component_access`] and /// [`Fetch::update_archetype_component_access`] exactly reflects the results of -/// [`FetchState::matches_component_set`], [`Fetch::archetype_fetch`], and -/// [`Fetch::table_fetch`]. +/// [`FetchState::matches_component_set`], [`Fetch::fetch`]. pub unsafe trait Fetch<'world>: Sized { type Item; type State: FetchState; From 4d27afc335cfed2ac53a746144dc39b43981ecd6 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 18 Jun 2022 01:22:34 -0700 Subject: [PATCH 20/42] Reference to value --- crates/bevy_ecs/src/query/fetch.rs | 35 ++++++++++++++--------------- crates/bevy_ecs/src/query/filter.rs | 16 ++++++------- crates/bevy_ecs/src/query/iter.rs | 16 ++++++------- crates/bevy_ecs/src/query/state.rs | 28 +++++++++++------------ 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index dd2af4fef14f2..8eca1da8b9748 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -423,7 +423,7 @@ pub unsafe trait Fetch<'world>: Sized { /// /// Must always be called _after_ [`Fetch::set_table`] or [`Fetch::set_archetype`]. `entity` and /// `table_row` must be in the range of the current table and archetype. - unsafe fn fetch(&mut self, entity: &Entity, table_index: &usize) -> Self::Item; + unsafe fn fetch(&mut self, entity: Entity, table_index: usize) -> Self::Item; /// # Safety /// @@ -431,7 +431,7 @@ pub unsafe trait Fetch<'world>: Sized { /// `table_row` must be in the range of the current table and archetype. #[allow(unused_variables)] #[inline(always)] - unsafe fn filter_fetch(&mut self, entity: &Entity, table_index: &usize) -> bool { + unsafe fn filter_fetch(&mut self, entity: Entity, table_index: usize) -> bool { true } @@ -524,8 +524,8 @@ unsafe impl<'w> Fetch<'w> for EntityFetch { unsafe fn set_table(&mut self, _state: &Self::State, _table: &'w Table) {} #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, _table_row: &usize) -> Self::Item { - *entity + unsafe fn fetch(&mut self, entity: Entity, _table_row: usize) -> Self::Item { + entity } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -658,17 +658,17 @@ unsafe impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { } #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => self .table_components .unwrap_or_else(|| debug_checked_unreachable()) - .get(*table_row) + .get(table_row) .deref(), StorageType::SparseSet => self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get(*entity) + .get(entity) .unwrap_or_else(|| debug_checked_unreachable()) .deref(), } @@ -803,10 +803,9 @@ unsafe impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { } #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { - let table_row = *table_row; let (table_components, table_ticks) = self .table_data .unwrap_or_else(|| debug_checked_unreachable()); @@ -823,7 +822,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { let (component, component_ticks) = self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_with_ticks(*entity) + .get_with_ticks(entity) .unwrap_or_else(|| debug_checked_unreachable()); Mut { value: component.assert_unique().deref_mut(), @@ -951,7 +950,7 @@ unsafe impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { } #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { self.matches.then(|| self.fetch.fetch(entity, table_row)) } @@ -1170,14 +1169,14 @@ unsafe impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { } #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => ChangeTrackers { component_ticks: { let table_ticks = self .table_ticks .unwrap_or_else(|| debug_checked_unreachable()); - table_ticks.get(*table_row).read() + table_ticks.get(table_row).read() }, marker: PhantomData, last_change_tick: self.last_change_tick, @@ -1187,7 +1186,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { component_ticks: self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(*entity) + .get_ticks(entity) .map(|ticks| &*ticks.get()) .cloned() .unwrap_or_else(|| debug_checked_unreachable()), @@ -1261,13 +1260,13 @@ macro_rules! impl_tuple_fetch { #[inline(always)] #[allow(clippy::unused_unit)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) -> Self::Item { let ($($name,)*) = self; ($($name.fetch(_entity, _table_row),)*) } #[inline(always)] - unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> bool { + unsafe fn filter_fetch(&mut self, _entity: Entity, _table_row: usize) -> bool { let ($($name,)*) = self; true $(&& $name.filter_fetch(_entity, _table_row))* } @@ -1375,7 +1374,7 @@ macro_rules! impl_anytuple_fetch { #[inline(always)] #[allow(clippy::unused_unit)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) -> Self::Item { let ($($name,)*) = &mut self.0; ($( $name.1.then(|| $name.0.fetch(_entity, _table_row)), @@ -1501,7 +1500,7 @@ unsafe impl<'w, State: FetchState> Fetch<'w> for NopFetch { unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item {} + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) -> Self::Item {} fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index fa44371c72fc9..8e69e81bbbe76 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -127,7 +127,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithFetch { } #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) {} + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) {} #[inline] fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { @@ -265,7 +265,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithoutFetch { } #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) {} + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) {} #[inline] fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { @@ -402,13 +402,13 @@ macro_rules! impl_query_filter_tuple { } #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) -> Self::Item { let ($($filter,)*) = &mut self.0; false $(|| ($filter.matches && $filter.fetch.filter_fetch(_entity, _table_row)))* } #[inline(always)] - unsafe fn filter_fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn filter_fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { self.fetch(entity, table_row) } @@ -578,13 +578,13 @@ macro_rules! impl_tick_filter { } #[inline(always)] - unsafe fn fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { $is_detected(&*( self.table_ticks .unwrap_or_else(|| debug_checked_unreachable()) - .get(*table_row)) + .get(table_row)) .deref(), self.last_change_tick, self.change_tick @@ -594,7 +594,7 @@ macro_rules! impl_tick_filter { let ticks = self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) - .get_ticks(*entity) + .get_ticks(entity) .map(|ticks| &*ticks.get()) .cloned() .unwrap_or_else(|| debug_checked_unreachable()); @@ -604,7 +604,7 @@ macro_rules! impl_tick_filter { } #[inline(always)] - unsafe fn filter_fetch(&mut self, entity: &Entity, table_row: &usize) -> Self::Item { + unsafe fn filter_fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { self.fetch(entity, table_row) } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index c3e551bc44729..0d3ba3d2a1de5 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -172,8 +172,8 @@ where .set_archetype(&self.query_state.fetch_state, archetype, self.tables); self.filter .set_archetype(&self.query_state.filter_state, archetype, self.tables); - if self.filter.filter_fetch(&entity.entity, &entity.table_row) { - return Some(self.fetch.fetch(&entity.entity, &entity.table_row)); + if self.filter.filter_fetch(entity.entity, entity.table_row) { + return Some(self.fetch.fetch(entity.entity, entity.table_row)); } } None @@ -464,12 +464,12 @@ where let index = self.current_index - 1; if Self::IS_DENSE { let entity = self.entities.get_unchecked(index); - Some(self.fetch.fetch(entity, &index)) + Some(self.fetch.fetch(*entity, index)) } else { let archetype_entity = self.archetype_entities.get_unchecked(index); Some( self.fetch - .fetch(&archetype_entity.entity, &archetype_entity.table_row), + .fetch(archetype_entity.entity, archetype_entity.table_row), ) } } else { @@ -500,12 +500,12 @@ where } let entity = self.entities.get_unchecked(self.current_index); - if !self.filter.filter_fetch(entity, &self.current_index) { + if !self.filter.filter_fetch(*entity, self.current_index) { self.current_index += 1; continue; } - let item = self.fetch.fetch(entity, &self.current_index); + let item = self.fetch.fetch(*entity, self.current_index); self.current_index += 1; return Some(item); } @@ -527,7 +527,7 @@ where let archetype_entity = self.archetype_entities.get_unchecked(self.current_index); if !self .filter - .filter_fetch(&archetype_entity.entity, &archetype_entity.table_row) + .filter_fetch(archetype_entity.entity, archetype_entity.table_row) { self.current_index += 1; continue; @@ -535,7 +535,7 @@ where let item = self .fetch - .fetch(&archetype_entity.entity, &archetype_entity.table_row); + .fetch(archetype_entity.entity, archetype_entity.table_row); self.current_index += 1; return Some(item); } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 245eab08b931e..5d23e1755e827 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -376,8 +376,8 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables); filter.set_archetype(&self.filter_state, archetype, &world.storages().tables); - if filter.filter_fetch(&entity, &location.index) { - Ok(fetch.fetch(&entity, &location.index)) + if filter.filter_fetch(entity, location.index) { + Ok(fetch.fetch(entity, location.index)) } else { Err(QueryEntityError::QueryDoesNotMatch(entity)) } @@ -899,10 +899,10 @@ impl QueryState { let entities = table.entities(); for row in 0..table.len() { let entity = entities.get_unchecked(row); - if !filter.filter_fetch(entity, &row) { + if !filter.filter_fetch(*entity, row) { continue; } - func(fetch.fetch(entity, &row)); + func(fetch.fetch(*entity, row)); } } } else { @@ -915,10 +915,10 @@ impl QueryState { let entities = archetype.entities(); for idx in 0..archetype.len() { let archetype_entity = entities.get_unchecked(idx); - if !filter.filter_fetch(&archetype_entity.entity, &archetype_entity.table_row) { + if !filter.filter_fetch(archetype_entity.entity, archetype_entity.table_row) { continue; } - func(fetch.fetch(&archetype_entity.entity, &archetype_entity.table_row)); + func(fetch.fetch(archetype_entity.entity, archetype_entity.table_row)); } } } @@ -977,10 +977,10 @@ impl QueryState { filter.set_table(&self.filter_state, table); for row in offset..offset + len { let entity = entities.get_unchecked(row); - if !filter.filter_fetch(entity, &row) { + if !filter.filter_fetch(*entity, row) { continue; } - let item = fetch.fetch(entity, &row); + let item = fetch.fetch(*entity, row); func(item); } }; @@ -1023,15 +1023,15 @@ impl QueryState { for archetype_index in offset..offset + len { let archetype_entity = entities.get_unchecked(archetype_index); if !filter.filter_fetch( - &archetype_entity.entity, - &archetype_entity.table_row, + archetype_entity.entity, + archetype_entity.table_row, ) { continue; } func( fetch.fetch( - &archetype_entity.entity, - &archetype_entity.table_row, + archetype_entity.entity, + archetype_entity.table_row, ), ); } @@ -1106,8 +1106,8 @@ impl QueryState { fetch.set_archetype(&self.fetch_state, archetype, tables); filter.set_archetype(&self.filter_state, archetype, tables); - if filter.filter_fetch(&entity.entity, &entity.table_row) { - func(fetch.fetch(&entity.entity, &entity.table_row)); + if filter.filter_fetch(entity.entity, entity.table_row) { + func(fetch.fetch(entity.entity, entity.table_row)); } } } From 50b52ae0affd8b747f2e1126022d021ed2de891d Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 21 Jun 2022 05:03:28 -0700 Subject: [PATCH 21/42] Formatting --- crates/bevy_ecs/src/query/state.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 5d23e1755e827..109e9b4717200 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1028,12 +1028,9 @@ impl QueryState { ) { continue; } - func( - fetch.fetch( - archetype_entity.entity, - archetype_entity.table_row, - ), - ); + let result = fetch + .fetch(archetype_entity.entity, archetype_entity.table_row); + func(result); } }; From 49ae4b6414c4589e8523c6d51dc798c9cf0367da Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 21 Jun 2022 05:47:41 -0700 Subject: [PATCH 22/42] Fix up WorldQuery derives --- crates/bevy_ecs/macros/src/fetch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs index e6f8515feabdb..afadd14baa35a 100644 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ b/crates/bevy_ecs/macros/src/fetch.rs @@ -227,7 +227,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { /// SAFETY: we call `fetch` for each member that implements `Fetch`. #[inline(always)] - unsafe fn fetch(&mut self, _entity: &Entity, _table_row: &usize) -> Self::Item { + unsafe fn fetch(&mut self, _entity: Entity, _table_row: usize) -> Self::Item { Self::Item { #(#field_idents: self.#field_idents.fetch(_entity, _table_row),)* #(#ignored_field_idents: Default::default(),)* @@ -236,7 +236,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { #[allow(unused_variables)] #[inline(always)] - unsafe fn filter_fetch(&mut self, _entity: &Entity, _table_row: &usize) -> bool { + unsafe fn filter_fetch(&mut self, _entity: Entity, _table_row: usize) -> bool { true #(&& self.#field_idents.filter_fetch(_entity, _table_row))* } From 255e3061c2752d87539135f75fb3e908bfdc42b9 Mon Sep 17 00:00:00 2001 From: Brian Merchant Date: Tue, 28 Jun 2022 04:09:04 +0000 Subject: [PATCH 23/42] Documenting `BufferVec`. (#4673) # Objective Documents the `BufferVec` render resource. `BufferVec` is a fairly low level object, that will likely be managed by a higher level API (e.g. through [`encase`](https://github.com/bevyengine/bevy/issues/4272)) in the future. For now, since it is still used by some simple example crates (e.g. [bevy-vertex-pulling](https://github.com/superdump/bevy-vertex-pulling)), it will be helpful to provide some simple documentation on what `BufferVec` does. ## Solution I looked through Discord discussion on `BufferVec`, and found [a comment](https://discord.com/channels/691052431525675048/953222550568173580/956596218857918464 ) by @superdump to be particularly helpful, in the general discussion around `encase`. I have taken care to clarify where the data is stored (host-side), when the device-side buffer is created (through calls to `reserve`), and when data writes from host to device are scheduled (using `write_buffer` calls). --- ## Changelog - Added doc string for `BufferVec` and two of its methods: `reserve` and `write_buffer`. Co-authored-by: Brian Merchant --- .../src/render_resource/buffer_vec.rs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/crates/bevy_render/src/render_resource/buffer_vec.rs b/crates/bevy_render/src/render_resource/buffer_vec.rs index 6e2deb3009018..80b88c7ac8197 100644 --- a/crates/bevy_render/src/render_resource/buffer_vec.rs +++ b/crates/bevy_render/src/render_resource/buffer_vec.rs @@ -6,6 +6,29 @@ use bevy_core::{cast_slice, Pod}; use copyless::VecHelper; use wgpu::BufferUsages; +/// A structure for storing raw bytes that have already been properly formatted +/// for use by the GPU. +/// +/// "Properly formatted" means that item data already meets the alignment and padding +/// requirements for how it will be used on the GPU. +/// +/// Index, vertex, and instance-rate vertex buffers have no alignment nor padding requirements and +/// so this helper type is a good choice for them. Uniform buffers must adhere to std140 +/// alignment/padding requirements, and storage buffers to std430. There are helper types for such +/// buffers: +/// - Uniform buffers +/// - Plain: [`UniformBuffer`](crate::render_resource::UniformBuffer) +/// - Dynamic offsets: [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer) +/// - Storage buffers +/// - Plain: [`StorageBuffer`](crate::render_resource::StorageBuffer) +/// - Dynamic offsets: [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer) +/// +/// The item type must implement [`Pod`] for its data representation to be directly copyable. +/// +/// The contained data is stored in system RAM. Calling [`reserve`](crate::render_resource::BufferVec::reserve) +/// allocates VRAM from the [`RenderDevice`](crate::renderer::RenderDevice). +/// [`write_buffer`](crate::render_resource::BufferVec::write_buffer) queues copying of the data +/// from system RAM to VRAM. pub struct BufferVec { values: Vec, buffer: Option, @@ -51,6 +74,17 @@ impl BufferVec { index } + /// Creates a [`Buffer`](crate::render_resource::Buffer) on the [`RenderDevice`](crate::renderer::RenderDevice) with size + /// at least `std::mem::size_of::() * capacity`, unless a such a buffer already exists. + /// + /// If a [`Buffer`](crate::render_resource::Buffer) exists, but is too small, references to it will be discarded, + /// and a new [`Buffer`](crate::render_resource::Buffer) will be created. Any previously created [`Buffer`](crate::render_resource::Buffer)s + /// that are no longer referenced will be deleted by the [`RenderDevice`](crate::renderer::RenderDevice) + /// once it is done using them (typically 1-2 frames). + /// + /// In addition to any [`BufferUsages`](crate::render_resource::BufferUsages) provided when + /// the `BufferVec` was created, the buffer on the [`RenderDevice`](crate::renderer::RenderDevice) + /// is marked as [`BufferUsages::COPY_DST`](crate::render_resource::BufferUsages). pub fn reserve(&mut self, capacity: usize, device: &RenderDevice) { if capacity > self.capacity { self.capacity = capacity; @@ -64,6 +98,11 @@ impl BufferVec { } } + /// Queues writing of data from system RAM to VRAM using the [`RenderDevice`](crate::renderer::RenderDevice) + /// and the provided [`RenderQueue`](crate::renderer::RenderQueue). + /// + /// Before queuing the write, a [`reserve`](crate::render_resource::BufferVec::reserve) operation + /// is executed. pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) { if self.values.is_empty() { return; From 696a1872dc9c07111c4cec1bc06878c91f1871ee Mon Sep 17 00:00:00 2001 From: Saverio Miroddi Date: Tue, 28 Jun 2022 16:37:36 +0000 Subject: [PATCH 24/42] Fix Events example link (#5126) The `crate` intermediate directory is missing from the path, which currently leads to 404. --- crates/bevy_ecs/src/event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 22af1b3ed2dff..ac473ab3f1b35 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -126,7 +126,7 @@ struct EventInstance { /// [`add_event`](https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event). /// /// [Example usage.](https://github.com/bevyengine/bevy/blob/latest/examples/ecs/event.rs) -/// [Example usage standalone.](https://github.com/bevyengine/bevy/blob/latest/bevy_ecs/examples/events.rs) +/// [Example usage standalone.](https://github.com/bevyengine/bevy/blob/latest/crates/bevy_ecs/examples/events.rs) /// #[derive(Debug)] pub struct Events { From d1e8773720b9e8fb67470593dcd55e484a0cdee1 Mon Sep 17 00:00:00 2001 From: harudagondi Date: Wed, 29 Jun 2022 02:15:28 +0000 Subject: [PATCH 25/42] Update `ExactSizeIterator` impl to support archetypal filters (With, Without) (#5124) # Objective - Fixes #3142 ## Solution - Done according to #3142 - Created new marker trait `ArchetypeFilter` - Implement said trait to: - `With` - `Without` - tuples containing only types that implement `ArchetypeFilter`, from 0 to 15 elements - `Or` where T is a tuple as described previously - Changed `ExactSizeIterator` impl to include a new generic that must implement `WorldQuery` and `ArchetypeFilter` - Added new tests --- ## Changelog ### Added - `Query`s with archetypal filters can now use `.iter().len()` to get the exact size of the iterator. --- crates/bevy_ecs/src/query/filter.rs | 27 ++++ crates/bevy_ecs/src/query/iter.rs | 11 +- crates/bevy_ecs/src/query/mod.rs | 141 ++++++++++++++++++ .../ui/query_exact_sized_iterator_safety.rs | 18 +++ .../query_exact_sized_iterator_safety.stderr | 29 ++++ 5 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs create mode 100644 crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 8e69e81bbbe76..f1cfa0d0d231b 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -722,3 +722,30 @@ impl_tick_filter!( ChangedFetch, ComponentTicks::is_changed ); + +/// A marker trait to indicate that the filter works at an archetype level. +/// +/// This is needed to implement [`ExactSizeIterator`](std::iter::ExactSizeIterator) for +/// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters. +/// +/// The trait must only be implement for filters where its corresponding [`Fetch::IS_ARCHETYPAL`](crate::query::Fetch::IS_ARCHETYPAL) +/// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait. +/// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types +/// also implement the same trait. +/// +/// [`Added`] and [`Changed`] works with entities, and therefore are not archetypal. As such +/// they do not implement [`ArchetypeFilter`]. +pub trait ArchetypeFilter {} + +impl ArchetypeFilter for With {} +impl ArchetypeFilter for Without {} + +macro_rules! impl_archetype_filter_tuple { + ($($filter: ident),*) => { + impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {} + + impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {} + }; +} + +all_tuples!(impl_archetype_filter_tuple, 0, 15, F); diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 0d3ba3d2a1de5..630e79be1144d 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -2,7 +2,7 @@ use crate::{ archetype::{ArchetypeEntity, ArchetypeId, Archetypes}, entity::{Entities, Entity}, prelude::World, - query::{Fetch, QueryState, WorldQuery}, + query::{ArchetypeFilter, Fetch, QueryState, WorldQuery}, storage::{TableId, Tables}, }; use std::{borrow::Borrow, iter::FusedIterator, marker::PhantomData, mem::MaybeUninit}; @@ -347,15 +347,10 @@ where } } -// NOTE: We can cheaply implement this for unfiltered Queries because we have: -// (1) pre-computed archetype matches -// (2) each archetype pre-computes length -// (3) there are no per-entity filters -// TODO: add an ArchetypeOnlyFilter that enables us to implement this for filters like With. -// This would need to be added to all types that implement Filter with Filter::IS_ARCHETYPAL = true -impl<'w, 's, Q: WorldQuery, QF> ExactSizeIterator for QueryIter<'w, 's, Q, QF, ()> +impl<'w, 's, Q: WorldQuery, QF, F> ExactSizeIterator for QueryIter<'w, 's, Q, QF, F> where QF: Fetch<'w, State = Q::State>, + F: WorldQuery + ArchetypeFilter, { fn len(&self) -> usize { self.query_state diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index d037c4a541974..74de0fd802bee 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -31,6 +31,8 @@ mod tests { struct B(usize); #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] struct C(usize); + #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] + struct D(usize); #[derive(Component, Debug, Eq, PartialEq, Clone, Copy)] #[component(storage = "SparseSet")] @@ -51,6 +53,145 @@ mod tests { assert_eq!(values, vec![&B(3)]); } + #[test] + fn query_filtered_len() { + let mut world = World::new(); + world.spawn().insert_bundle((A(1), B(1))); + world.spawn().insert_bundle((A(2),)); + world.spawn().insert_bundle((A(3),)); + + let mut values = world.query_filtered::<&A, With>(); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Without>(); + let n = 2; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + let mut world = World::new(); + world.spawn().insert_bundle((A(1), B(1), C(1))); + world.spawn().insert_bundle((A(2), B(2))); + world.spawn().insert_bundle((A(3), B(3))); + world.spawn().insert_bundle((A(4), C(4))); + world.spawn().insert_bundle((A(5), C(5))); + world.spawn().insert_bundle((A(6), C(6))); + world.spawn().insert_bundle((A(7),)); + world.spawn().insert_bundle((A(8),)); + world.spawn().insert_bundle((A(9),)); + world.spawn().insert_bundle((A(10),)); + + // With/Without for B and C + let mut values = world.query_filtered::<&A, With>(); + let n = 3; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, With>(); + let n = 4; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Without>(); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Without>(); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + // With/Without (And) combinations + let mut values = world.query_filtered::<&A, (With, With)>(); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, (With, Without)>(); + let n = 2; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, (Without, With)>(); + let n = 3; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, (Without, Without)>(); + let n = 4; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + // With/Without Or<()> combinations + let mut values = world.query_filtered::<&A, Or<(With, With)>>(); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(With, Without)>>(); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Without, With)>>(); + let n = 8; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Without, Without)>>(); + let n = 9; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + let mut values = world.query_filtered::<&A, (Or<(With,)>, Or<(With,)>)>(); + let n = 1; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, With)>>(); + let n = 6; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + + world.spawn().insert_bundle((A(11), D(11))); + + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, With)>>(); + let n = 7; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + let mut values = world.query_filtered::<&A, Or<(Or<(With, With)>, Without)>>(); + let n = 10; + assert_eq!(values.iter(&world).size_hint().0, n); + assert_eq!(values.iter(&world).size_hint().1.unwrap(), n); + assert_eq!(values.iter(&world).len(), n); + assert_eq!(values.iter(&world).count(), n); + } + #[test] fn query_iter_combinations() { let mut world = World::new(); diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs new file mode 100644 index 0000000000000..e634bd86d217c --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.rs @@ -0,0 +1,18 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct Foo; + +fn on_changed(query: Query<&Foo, Changed>) { + // this should fail to compile + is_exact_size_iterator(query.iter()); +} + +fn on_added(query: Query<&Foo, Added>) { + // this should fail to compile + is_exact_size_iterator(query.iter()); +} + +fn is_exact_size_iterator(_iter: T) {} + +fn main() {} \ No newline at end of file diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr new file mode 100644 index 0000000000000..22ea39040d131 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/query_exact_sized_iterator_safety.stderr @@ -0,0 +1,29 @@ +error[E0277]: the trait bound `bevy_ecs::query::Changed: ArchetypeFilter` is not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:8:28 + | +8 | is_exact_size_iterator(query.iter()); + | ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Changed` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Changed>` +note: required by a bound in `is_exact_size_iterator` + --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 + | +16 | fn is_exact_size_iterator(_iter: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator` + +error[E0277]: the trait bound `bevy_ecs::query::Added: ArchetypeFilter` is not satisfied + --> tests/ui/query_exact_sized_iterator_safety.rs:13:28 + | +13 | is_exact_size_iterator(query.iter()); + | ---------------------- ^^^^^^^^^^^^ the trait `ArchetypeFilter` is not implemented for `bevy_ecs::query::Added` + | | + | required by a bound introduced by this call + | + = note: required because of the requirements on the impl of `ExactSizeIterator` for `QueryIter<'_, '_, &Foo, ReadFetch<'_, Foo>, bevy_ecs::query::Added>` +note: required by a bound in `is_exact_size_iterator` + --> tests/ui/query_exact_sized_iterator_safety.rs:16:30 + | +16 | fn is_exact_size_iterator(_iter: T) {} + | ^^^^^^^^^^^^^^^^^ required by this bound in `is_exact_size_iterator` From ba28b0d8c45365a9bfa25f0bbac89dc91baee673 Mon Sep 17 00:00:00 2001 From: James Liu Date: Wed, 29 Jun 2022 02:29:50 +0000 Subject: [PATCH 26/42] Wider ECS Benchmarks (#5123) # Objective As a part of evaluating #4800, at the behest of @cart, it was noted that the ECS microbenchmarks all focus on singular component queries, whereas in reality most systems will have wider queries with multiple components in each. ## Solution Use const generics to add wider variants of existing benchmarks. --- .../ecs_bench_suite/frag_iter_foreach_wide.rs | 70 ++++++++++++++++ .../ecs_bench_suite/frag_iter_wide.rs | 70 ++++++++++++++++ .../benches/bevy_ecs/ecs_bench_suite/mod.rs | 40 ++++++++++ .../simple_iter_foreach_wide.rs | 63 +++++++++++++++ .../simple_iter_sparse_foreach_wide.rs | 65 +++++++++++++++ .../simple_iter_sparse_wide.rs | 65 +++++++++++++++ .../ecs_bench_suite/simple_iter_wide.rs | 63 +++++++++++++++ .../sparse_frag_iter_foreach_wide.rs | 80 +++++++++++++++++++ .../ecs_bench_suite/sparse_frag_iter_wide.rs | 80 +++++++++++++++++++ benches/benches/bevy_ecs/world_get.rs | 63 +++++++++++++++ 10 files changed, 659 insertions(+) create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach_wide.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_wide.rs diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach_wide.rs new file mode 100644 index 0000000000000..ee8f93c310ea6 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach_wide.rs @@ -0,0 +1,70 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(( + $variants(0.0), + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0.0 *= 2.0; + data.1.0 *= 2.0; + data.2.0 *= 2.0; + data.3.0 *= 2.0; + data.4.0 *= 2.0; + data.5.0 *= 2.0; + data.6.0 *= 2.0; + data.7.0 *= 2.0; + data.8.0 *= 2.0; + data.9.0 *= 2.0; + data.10.0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_wide.rs new file mode 100644 index 0000000000000..d7514a07d6201 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_wide.rs @@ -0,0 +1,70 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(( + $variants(0.0), + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0.0 *= 2.0; + data.1.0 *= 2.0; + data.2.0 *= 2.0; + data.3.0 *= 2.0; + data.4.0 *= 2.0; + data.5.0 *= 2.0; + data.6.0 *= 2.0; + data.7.0 *= 2.0; + data.8.0 *= 2.0; + data.9.0 *= 2.0; + data.10.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs b/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs index bb127d63a7cca..bad7c86e5b44f 100644 --- a/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs +++ b/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs @@ -5,7 +5,9 @@ mod add_remove_big_table; mod add_remove_sparse_set; mod add_remove_table; mod frag_iter; +mod frag_iter_wide; mod frag_iter_foreach; +mod frag_iter_foreach_wide; mod get_component; mod get_component_system; mod heavy_compute; @@ -13,12 +15,18 @@ mod schedule; mod simple_insert; mod simple_insert_unbatched; mod simple_iter; +mod simple_iter_wide; mod simple_iter_foreach; +mod simple_iter_foreach_wide; mod simple_iter_sparse; +mod simple_iter_sparse_wide; mod simple_iter_sparse_foreach; +mod simple_iter_sparse_foreach_wide; mod simple_iter_system; mod sparse_frag_iter; +mod sparse_frag_iter_wide; mod sparse_frag_iter_foreach; +mod sparse_frag_iter_foreach_wide; fn bench_simple_insert(c: &mut Criterion) { let mut group = c.benchmark_group("simple_insert"); @@ -43,6 +51,10 @@ fn bench_simple_iter(c: &mut Criterion) { let mut bench = simple_iter::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("wide", |b| { + let mut bench = simple_iter_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.bench_function("system", |b| { let mut bench = simple_iter_system::Benchmark::new(); b.iter(move || bench.run()); @@ -51,14 +63,26 @@ fn bench_simple_iter(c: &mut Criterion) { let mut bench = simple_iter_sparse::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("sparse_wide", |b| { + let mut bench = simple_iter_sparse_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.bench_function("foreach", |b| { let mut bench = simple_iter_foreach::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("foreach_wide", |b| { + let mut bench = simple_iter_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.bench_function("sparse_foreach", |b| { let mut bench = simple_iter_sparse_foreach::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("sparse_foreach_wide", |b| { + let mut bench = simple_iter_sparse_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.finish(); } @@ -70,10 +94,18 @@ fn bench_frag_iter_bc(c: &mut Criterion) { let mut bench = frag_iter::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("wide", |b| { + let mut bench = frag_iter_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.bench_function("foreach", |b| { let mut bench = frag_iter_foreach::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("foreach_wide", |b| { + let mut bench = frag_iter_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.finish(); } @@ -85,10 +117,18 @@ fn bench_sparse_frag_iter(c: &mut Criterion) { let mut bench = sparse_frag_iter::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("wide", |b| { + let mut bench = sparse_frag_iter_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.bench_function("foreach", |b| { let mut bench = sparse_frag_iter_foreach::Benchmark::new(); b.iter(move || bench.run()); }); + group.bench_function("foreach_wide", |b| { + let mut bench = sparse_frag_iter_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); group.finish(); } diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach_wide.rs new file mode 100644 index 0000000000000..3a5590baececc --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach_wide.rs @@ -0,0 +1,63 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut item| { + item.1.0 += item.0.0; + item.3.0 += item.2.0; + item.5.0 += item.4.0; + item.7.0 += item.6.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach_wide.rs new file mode 100644 index 0000000000000..a74dea68a7b22 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach_wide.rs @@ -0,0 +1,65 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut item| { + item.1.0 += item.0.0; + item.3.0 += item.2.0; + item.5.0 += item.4.0; + item.7.0 += item.6.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_wide.rs new file mode 100644 index 0000000000000..850d82bce2870 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_wide.rs @@ -0,0 +1,65 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut item in self.1.iter_mut(&mut self.0) { + item.1.0 += item.0.0; + item.3.0 += item.2.0; + item.5.0 += item.4.0; + item.7.0 += item.6.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_wide.rs new file mode 100644 index 0000000000000..b4ae51d8918a4 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_wide.rs @@ -0,0 +1,63 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut item in self.1.iter_mut(&mut self.0) { + item.1.0 += item.0.0; + item.3.0 += item.2.0; + item.5.0 += item.4.0; + item.7.0 += item.6.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach_wide.rs new file mode 100644 index 0000000000000..e8b52de5054c1 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach_wide.rs @@ -0,0 +1,80 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + for _ in 0..5 { + world.spawn().insert_bundle(( + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0.0 *= 2.0; + data.1.0 *= 2.0; + data.2.0 *= 2.0; + data.3.0 *= 2.0; + data.4.0 *= 2.0; + data.5.0 *= 2.0; + data.6.0 *= 2.0; + data.7.0 *= 2.0; + data.8.0 *= 2.0; + data.9.0 *= 2.0; + data.10.0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_wide.rs b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_wide.rs new file mode 100644 index 0000000000000..8074363e4e0fc --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_wide.rs @@ -0,0 +1,80 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, +)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + for _ in 0..5 { + world.spawn().insert_bundle(( + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0.0 *= 2.0; + data.1.0 *= 2.0; + data.2.0 *= 2.0; + data.3.0 *= 2.0; + data.4.0 *= 2.0; + data.5.0 *= 2.0; + data.6.0 *= 2.0; + data.7.0 *= 2.0; + data.8.0 *= 2.0; + data.9.0 *= 2.0; + data.10.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/world_get.rs b/benches/benches/bevy_ecs/world_get.rs index f96df193e7a0a..2b5c6f4e507ef 100644 --- a/benches/benches/bevy_ecs/world_get.rs +++ b/benches/benches/bevy_ecs/world_get.rs @@ -2,6 +2,7 @@ use bevy_ecs::{ component::Component, entity::Entity, system::{Query, SystemState}, + bundle::Bundle, world::World, }; use criterion::{black_box, criterion_group, criterion_main, Criterion}; @@ -26,6 +27,12 @@ struct Table(f32); #[derive(Component, Default)] #[component(storage = "SparseSet")] struct Sparse(f32); +#[derive(Component, Default)] +#[component(storage = "Table")] +struct WideTable(f32); +#[derive(Component, Default)] +#[component(storage = "SparseSet")] +struct WideSparse(f32); const RANGE: std::ops::Range = 5..6; @@ -39,6 +46,12 @@ fn setup(entity_count: u32) -> World { black_box(world) } +fn setup_wide(entity_count: u32) -> World { + let mut world = World::default(); + world.spawn_batch((0..entity_count).map(|_| T::default())); + black_box(world) +} + fn world_entity(criterion: &mut Criterion) { let mut group = criterion.benchmark_group("world_entity"); group.warm_up_time(std::time::Duration::from_millis(500)); @@ -108,10 +121,60 @@ fn world_query_get(criterion: &mut Criterion) { } }); }); + group.bench_function(format!("{}_entities_table_wide", entity_count), |bencher| { + let mut world = setup_wide::<( + WideTable<0>, + WideTable<1>, + WideTable<2>, + WideTable<3>, + WideTable<4>, + WideTable<5>, + )>(entity_count); + let mut query = world.query::<( + &WideTable<0>, + &WideTable<1>, + &WideTable<2>, + &WideTable<3>, + &WideTable<4>, + &WideTable<5>, + )>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { let mut world = setup::(entity_count); let mut query = world.query::<&Sparse>(); + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); + group.bench_function(format!("{}_entities_sparse_wide", entity_count), |bencher| { + let mut world = setup_wide::<( + WideSparse<0>, + WideSparse<1>, + WideSparse<2>, + WideSparse<3>, + WideSparse<4>, + WideSparse<5>, + )>(entity_count); + let mut query = world.query::<( + &WideSparse<0>, + &WideSparse<1>, + &WideSparse<2>, + &WideSparse<3>, + &WideSparse<4>, + &WideSparse<5>, + )>(); + bencher.iter(|| { for i in 0..entity_count { let entity = Entity::from_raw(i); From cce14dda0ddbac940267651a670c5d18b4afe9ac Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 29 Jun 2022 02:29:51 +0000 Subject: [PATCH 27/42] fix resource not found error message (#5128) There are some outdated error messages for when a resource is not found. It references `add_resource` and `add_non_send_resource` which were renamed to `insert_resource` and `insert_non_send_resource`. --- crates/bevy_ecs/src/world/mod.rs | 8 ++++---- crates/bevy_ecs/src/world/world_cell.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 73d67a3174a86..5c75740f7bdfa 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -791,7 +791,7 @@ impl World { Some(x) => x, None => panic!( "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -815,7 +815,7 @@ impl World { Some(x) => x, None => panic!( "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -877,7 +877,7 @@ impl World { Some(x) => x, None => panic!( "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), @@ -897,7 +897,7 @@ impl World { Some(x) => x, None => panic!( "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index ba4d0b701103d..c40a852a0236f 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -204,7 +204,7 @@ impl<'w> WorldCell<'w> { Some(x) => x, None => panic!( "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -239,7 +239,7 @@ impl<'w> WorldCell<'w> { Some(x) => x, None => panic!( "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -272,7 +272,7 @@ impl<'w> WorldCell<'w> { Some(x) => x, None => panic!( "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), @@ -307,7 +307,7 @@ impl<'w> WorldCell<'w> { Some(x) => x, None => panic!( "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), From da59bbd028a102e03d338f2ee72ad860dcf4e9a5 Mon Sep 17 00:00:00 2001 From: DGriffin91 <33357138+DGriffin91@users.noreply.github.com> Date: Wed, 29 Jun 2022 02:48:46 +0000 Subject: [PATCH 28/42] Move texture sample out of branch in prepare_normal (#5129) # Objective This fixes https://github.com/bevyengine/bevy/issues/5127 ## Solution - Moved texture sample out of branch in `prepare_normal()`. Co-authored-by: DGriffin91 --- crates/bevy_pbr/src/render/pbr_functions.wgsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index b389801b61b46..f35d2a3d6b375 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -41,13 +41,13 @@ fn prepare_normal( #ifdef VERTEX_TANGENTS #ifdef STANDARDMATERIAL_NORMAL_MAP // Nt is the tangent-space normal. - var Nt: vec3; + var Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb; if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u) { // Only use the xy components and derive z for 2-component normal maps. - Nt = vec3(textureSample(normal_map_texture, normal_map_sampler, uv).rg * 2.0 - 1.0, 0.0); + Nt = vec3(Nt.rg * 2.0 - 1.0, 0.0); Nt.z = sqrt(1.0 - Nt.x * Nt.x - Nt.y * Nt.y); } else { - Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb * 2.0 - 1.0; + Nt = Nt * 2.0 - 1.0; } // Normal maps authored for DirectX require flipping the y component if ((standard_material_flags & STANDARD_MATERIAL_FLAGS_FLIP_NORMAL_MAP_Y) != 0u) { From 5bc5adbd67c516081b573f2c3404c58b400f9123 Mon Sep 17 00:00:00 2001 From: grace125 Date: Wed, 29 Jun 2022 02:48:47 +0000 Subject: [PATCH 29/42] Fix typos in bevy_reflect readme (#5134) # Objective Fix some typos in bevy_reflect's readme ## Solution - Change `Foo`'s `d` field to be of type `Vec` - Format `&dyn Reflect` to be monospace --- crates/bevy_reflect/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/README.md b/crates/bevy_reflect/README.md index b36933051dcce..975ff290d39aa 100644 --- a/crates/bevy_reflect/README.md +++ b/crates/bevy_reflect/README.md @@ -21,7 +21,7 @@ struct Foo { a: u32, b: Bar, c: Vec, - d: Vec, + d: Vec, } // this will automatically implement the Reflect trait and the TupleStruct trait (because the type is a tuple struct) @@ -107,7 +107,7 @@ assert!(foo.reflect_partial_eq(&dynamic_struct).unwrap()); ### Trait "reflection" -Call a trait on a given &dyn Reflect reference without knowing the underlying type! +Call a trait on a given `&dyn Reflect` reference without knowing the underlying type! ```rust ignore #[derive(Reflect)] From b32681c172e44daf5b41647016ace0da5a09318e Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 2 Jul 2022 01:13:39 -0700 Subject: [PATCH 30/42] Eagerly fetch table for set_archetype --- crates/bevy_ecs/src/query/fetch.rs | 65 +++++++++-------------------- crates/bevy_ecs/src/query/filter.rs | 22 ++++------ crates/bevy_ecs/src/query/iter.rs | 10 +++-- crates/bevy_ecs/src/query/state.rs | 20 +++++---- 4 files changed, 46 insertions(+), 71 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 8eca1da8b9748..036bb9dc25b29 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -4,7 +4,7 @@ use crate::{ component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType}, entity::Entity, query::{debug_checked_unreachable, Access, FilteredAccess}, - storage::{ComponentSparseSet, Table, Tables}, + storage::{ComponentSparseSet, Table}, world::{Mut, World}, }; use bevy_ecs_macros::all_tuples; @@ -402,7 +402,7 @@ pub unsafe trait Fetch<'world>: Sized { &mut self, state: &Self::State, archetype: &'world Archetype, - tables: &'world Tables, + table: &'world Table, ); /// Adjusts internal state to account for the next [`Table`]. This will always be called on tables @@ -516,7 +516,7 @@ unsafe impl<'w> Fetch<'w> for EntityFetch { &mut self, _state: &Self::State, _archetype: &'w Archetype, - _tables: &Tables, + _table: &Table, ) { } @@ -632,18 +632,10 @@ unsafe impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { unsafe fn set_archetype( &mut self, state: &Self::State, - archetype: &'w Archetype, - tables: &'w Tables, + _archetype: &'w Archetype, + table: &'w Table, ) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let column = tables[archetype.table_id()] - .get_column(state.component_id) - .unwrap(); - self.table_components = Some(column.get_data_slice().into()); - } - StorageType::SparseSet => {} - } + self.set_table(state, table); } #[inline] @@ -776,21 +768,10 @@ unsafe impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { unsafe fn set_archetype( &mut self, state: &Self::State, - archetype: &'w Archetype, - tables: &'w Tables, + _archetype: &'w Archetype, + table: &'w Table, ) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let column = tables[archetype.table_id()] - .get_column(state.component_id) - .unwrap(); - self.table_data = Some(( - column.get_data_slice().into(), - column.get_ticks_slice().into(), - )); - } - StorageType::SparseSet => {} - } + self.set_table(state, table); } #[inline] @@ -929,13 +910,13 @@ unsafe impl<'w, T: Fetch<'w>> Fetch<'w> for OptionFetch { &mut self, state: &Self::State, archetype: &'w Archetype, - tables: &'w Tables, + table: &'w Table, ) { self.matches = state .state .matches_component_set(&|id| archetype.contains(id)); if self.matches { - self.fetch.set_archetype(&state.state, archetype, tables); + self.fetch.set_archetype(&state.state, archetype, table); } } @@ -1143,18 +1124,10 @@ unsafe impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { unsafe fn set_archetype( &mut self, state: &Self::State, - archetype: &'w Archetype, - tables: &'w Tables, + _archetype: &'w Archetype, + table: &'w Table, ) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let column = tables[archetype.table_id()] - .get_column(state.component_id) - .unwrap(); - self.table_ticks = Some(column.get_ticks_slice().into()); - } - StorageType::SparseSet => {} - } + self.set_table(state, table); } #[inline] @@ -1245,10 +1218,10 @@ macro_rules! impl_tuple_fetch { const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; #[inline] - unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &'w Archetype, _tables: &'w Tables) { + unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &'w Archetype, _table: &'w Table) { let ($($name,)*) = self; let ($($state,)*) = _state; - $($name.set_archetype($state, _archetype, _tables);)* + $($name.set_archetype($state, _archetype, _table);)* } #[inline] @@ -1349,13 +1322,13 @@ macro_rules! impl_anytuple_fetch { const IS_ARCHETYPAL: bool = true $(&& $name::IS_ARCHETYPAL)*; #[inline] - unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &'w Archetype, _tables: &'w Tables) { + unsafe fn set_archetype(&mut self, _state: &Self::State, _archetype: &'w Archetype, _table: &'w Table) { let ($($name,)*) = &mut self.0; let ($($state,)*) = &_state.0; $( $name.1 = $state.matches_component_set(&|id| _archetype.contains(id)); if $name.1 { - $name.0.set_archetype($state, _archetype, _tables); + $name.0.set_archetype($state, _archetype, _table); } )* } @@ -1492,7 +1465,7 @@ unsafe impl<'w, State: FetchState> Fetch<'w> for NopFetch { &mut self, _state: &Self::State, _archetype: &Archetype, - _tables: &Tables, + _tables: &Table, ) { } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index f1cfa0d0d231b..5753b80123006 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -6,7 +6,7 @@ use crate::{ debug_checked_unreachable, Access, Fetch, FetchState, FilteredAccess, QueryFetch, WorldQuery, WorldQueryGats, }, - storage::{ComponentSparseSet, Table, Tables}, + storage::{ComponentSparseSet, Table}, world::World, }; use bevy_ecs_macros::all_tuples; @@ -122,7 +122,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithFetch { &mut self, _state: &Self::State, _archetype: &Archetype, - _tables: &Tables, + _table: &Table, ) { } @@ -260,7 +260,7 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithoutFetch { &mut self, _state: &Self::State, _archetype: &Archetype, - _tables: &Tables, + _table: &Table, ) { } @@ -390,13 +390,13 @@ macro_rules! impl_query_filter_tuple { } #[inline] - unsafe fn set_archetype(&mut self, state: & Self::State, archetype: &'w Archetype, tables: &'w Tables) { + unsafe fn set_archetype(&mut self, state: & Self::State, archetype: &'w Archetype, table: &'w Table) { let ($($filter,)*) = &mut self.0; let ($($state,)*) = &state.0; $( $filter.matches = $state.matches_component_set(&|id| archetype.contains(id)); if $filter.matches { - $filter.fetch.set_archetype($state, archetype, tables); + $filter.fetch.set_archetype($state, archetype, table); } )* } @@ -558,6 +558,7 @@ macro_rules! impl_tick_filter { const IS_ARCHETYPAL: bool = false; + #[inline] unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) { self.table_ticks = Some( table.get_column(state.component_id) @@ -567,14 +568,9 @@ macro_rules! impl_tick_filter { ); } - unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) { - match T::Storage::STORAGE_TYPE { - StorageType::Table => { - let table = &tables[archetype.table_id()]; - self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into()); - } - StorageType::SparseSet => {}, - } + #[inline] + unsafe fn set_archetype(&mut self, state: &Self::State, _archetype: &'w Archetype, table: &'w Table) { + self.set_table(state, table); } #[inline(always)] diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index ea916fff43b7c..ff64500a51a99 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -166,12 +166,13 @@ where } let archetype = &self.archetypes[location.archetype_id]; + let table = &self.tables[archetype.table_id()]; let entity = &archetype.entities().get_unchecked(location.index); self.fetch - .set_archetype(&self.query_state.fetch_state, archetype, self.tables); + .set_archetype(&self.query_state.fetch_state, archetype, table); self.filter - .set_archetype(&self.query_state.filter_state, archetype, self.tables); + .set_archetype(&self.query_state.filter_state, archetype, table); if self.filter.filter_fetch(entity.entity, entity.table_row) { return Some(self.fetch.fetch(entity.entity, entity.table_row)); } @@ -509,10 +510,11 @@ where if self.current_index == self.current_len { let archetype_id = self.archetype_id_iter.next()?; let archetype = &archetypes[*archetype_id]; + let table = &tables[archetype.table_id()]; self.fetch - .set_archetype(&query_state.fetch_state, archetype, tables); + .set_archetype(&query_state.fetch_state, archetype, table); self.filter - .set_archetype(&query_state.filter_state, archetype, tables); + .set_archetype(&query_state.filter_state, archetype, table); self.archetype_entities = archetype.entities(); self.current_len = archetype.len(); self.current_index = 0; diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 406a15de441c2..463d0901d430f 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -374,8 +374,9 @@ impl QueryState { change_tick, ); - fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables); - filter.set_archetype(&self.filter_state, archetype, &world.storages().tables); + let table = &world.storages().tables[archetype.table_id()]; + fetch.set_archetype(&self.fetch_state, archetype, table); + filter.set_archetype(&self.filter_state, archetype, table); if filter.filter_fetch(entity, location.index) { Ok(fetch.fetch(entity, location.index)) } else { @@ -909,8 +910,9 @@ impl QueryState { let archetypes = &world.archetypes; for archetype_id in &self.matched_archetype_ids { let archetype = &archetypes[*archetype_id]; - fetch.set_archetype(&self.fetch_state, archetype, tables); - filter.set_archetype(&self.filter_state, archetype, tables); + let table = &tables[archetype.table_id()]; + fetch.set_archetype(&self.fetch_state, archetype, table); + filter.set_archetype(&self.filter_state, archetype, table); let entities = archetype.entities(); for idx in 0..archetype.len() { @@ -1016,8 +1018,9 @@ impl QueryState { ); let tables = &world.storages().tables; let archetype = &world.archetypes[*archetype_id]; - fetch.set_archetype(&self.fetch_state, archetype, tables); - filter.set_archetype(&self.filter_state, archetype, tables); + let table = &tables[archetype.table_id()]; + fetch.set_archetype(&self.fetch_state, archetype, table); + filter.set_archetype(&self.filter_state, archetype, table); let entities = archetype.entities(); for archetype_index in offset..offset + len { @@ -1099,10 +1102,11 @@ impl QueryState { } let archetype = &world.archetypes[location.archetype_id]; + let table = &tables[archetype.table_id()]; let entity = &archetype.entities().get_unchecked(location.index); - fetch.set_archetype(&self.fetch_state, archetype, tables); - filter.set_archetype(&self.filter_state, archetype, tables); + fetch.set_archetype(&self.fetch_state, archetype, table); + filter.set_archetype(&self.filter_state, archetype, table); if filter.filter_fetch(entity.entity, entity.table_row) { func(fetch.fetch(entity.entity, entity.table_row)); } From 38f5ea4fd4e5888b2200d2e07f3e3b372c6afbc0 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 2 Jul 2022 01:18:03 -0700 Subject: [PATCH 31/42] Fix WorldQuery macro --- crates/bevy_ecs/macros/src/fetch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs index 201f6760a92b2..d158d698078d6 100644 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ b/crates/bevy_ecs/macros/src/fetch.rs @@ -214,9 +214,9 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { &mut self, _state: &Self::State, _archetype: &'__w #path::archetype::Archetype, - _tables: &'__w #path::storage::Tables + _table: &'__w #path::storage::Table ) { - #(self.#field_idents.set_archetype(&_state.#field_idents, _archetype, _tables);)* + #(self.#field_idents.set_archetype(&_state.#field_idents, _archetype, _table);)* } /// SAFETY: we call `set_table` for each member that implements `Fetch` From 815072fb6339ffe3195b2193568a9c474296371f Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 2 Jul 2022 01:36:58 -0700 Subject: [PATCH 32/42] Set table inside set_archetype only if dense --- crates/bevy_ecs/src/query/fetch.rs | 12 +++++++++--- crates/bevy_ecs/src/query/filter.rs | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 036bb9dc25b29..bb191fab074da 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -635,7 +635,9 @@ unsafe impl<'w, T: Component> Fetch<'w> for ReadFetch<'w, T> { _archetype: &'w Archetype, table: &'w Table, ) { - self.set_table(state, table); + if Self::IS_DENSE { + self.set_table(state, table); + } } #[inline] @@ -771,7 +773,9 @@ unsafe impl<'w, T: Component> Fetch<'w> for WriteFetch<'w, T> { _archetype: &'w Archetype, table: &'w Table, ) { - self.set_table(state, table); + if Self::IS_DENSE { + self.set_table(state, table); + } } #[inline] @@ -1127,7 +1131,9 @@ unsafe impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { _archetype: &'w Archetype, table: &'w Table, ) { - self.set_table(state, table); + if Self::IS_DENSE { + self.set_table(state, table); + } } #[inline] diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 5753b80123006..91a485d711fd0 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -570,7 +570,9 @@ macro_rules! impl_tick_filter { #[inline] unsafe fn set_archetype(&mut self, state: &Self::State, _archetype: &'w Archetype, table: &'w Table) { - self.set_table(state, table); + if Self::IS_DENSE { + self.set_table(state, table); + } } #[inline(always)] From 0dbf1c99436e2806e6294c61f94684ad4e20d6fd Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 2 Jul 2022 01:43:23 -0700 Subject: [PATCH 33/42] Reduce Option mapping when getting ticks --- crates/bevy_ecs/src/query/fetch.rs | 7 +++---- crates/bevy_ecs/src/query/filter.rs | 9 ++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index bb191fab074da..36672b965b239 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1162,13 +1162,12 @@ unsafe impl<'w, T: Component> Fetch<'w> for ChangeTrackersFetch<'w, T> { change_tick: self.change_tick, }, StorageType::SparseSet => ChangeTrackers { - component_ticks: self + component_ticks: *self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) .get_ticks(entity) - .map(|ticks| &*ticks.get()) - .cloned() - .unwrap_or_else(|| debug_checked_unreachable()), + .unwrap_or_else(|| debug_checked_unreachable()) + .get(), marker: PhantomData, last_change_tick: self.last_change_tick, change_tick: self.change_tick, diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 91a485d711fd0..7e547f624d7fb 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -589,14 +589,13 @@ macro_rules! impl_tick_filter { ) } StorageType::SparseSet => { - let ticks = self + let ticks = &*self .sparse_set .unwrap_or_else(|| debug_checked_unreachable()) .get_ticks(entity) - .map(|ticks| &*ticks.get()) - .cloned() - .unwrap_or_else(|| debug_checked_unreachable()); - $is_detected(&ticks, self.last_change_tick, self.change_tick) + .unwrap_or_else(|| debug_checked_unreachable()) + .get(); + $is_detected(ticks, self.last_change_tick, self.change_tick) } } } From eb2c6a4fde5d36b00f6c2602be9a5ce2cf4c110b Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 15:01:28 -0700 Subject: [PATCH 34/42] Fix build --- crates/bevy_ecs/src/query/fetch.rs | 80 ++++++++++++++--------------- crates/bevy_ecs/src/query/filter.rs | 53 ++++++++++--------- crates/bevy_ecs/src/query/iter.rs | 22 ++++---- crates/bevy_ecs/src/query/state.rs | 22 ++------ crates/bevy_ecs/src/world/mod.rs | 3 +- crates/bevy_scene/src/scene.rs | 4 +- 6 files changed, 90 insertions(+), 94 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 16c727ac4ebb6..230a6e63fecc4 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -285,11 +285,10 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// exactly reflects the results of the following methods: /// /// - [`matches_component_set`] -/// - [`archetype_fetch`] -/// - [`table_fetch`] +/// - [`fetch`] /// /// [`Added`]: crate::query::Added -/// [`archetype_fetch`]: Self::archetype_fetch +/// [`fetch`]: Self::fetch /// [`Changed`]: crate::query::Changed /// [`Fetch`]: crate::query::WorldQueryGats::Fetch /// [`matches_component_set`]: Self::matches_component_set @@ -297,7 +296,6 @@ use std::{cell::UnsafeCell, marker::PhantomData}; /// [`Query`]: crate::system::Query /// [`ReadOnly`]: Self::ReadOnly /// [`State`]: Self::State -/// [`table_fetch`]: Self::table_fetch /// [`update_archetype_component_access`]: Self::update_archetype_component_access /// [`update_component_access`]: Self::update_component_access /// [`With`]: crate::query::With @@ -381,7 +379,7 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { fetch: &mut >::Fetch, entity: Entity, table_index: usize, - ) -> Self::Item; + ) -> >::Item; /// # Safety /// @@ -467,7 +465,7 @@ unsafe impl WorldQuery for Entity { #[inline] unsafe fn set_archetype<'w>( - fetch: &mut EntityFetch, + _fetch: &mut EntityFetch, _state: &(), _archetype: &'w Archetype, _table: &Table, @@ -478,7 +476,11 @@ unsafe impl WorldQuery for Entity { unsafe fn set_table<'w>(_fetch: &mut EntityFetch, _state: &(), _table: &'w Table) {} #[inline(always)] - unsafe fn fetch<'w>(_fetch: &mut EntityFetch, entity: Entity, _table_row: usize) -> Self::Item { + unsafe fn fetch<'w>( + _fetch: &mut EntityFetch, + entity: Entity, + _table_row: usize, + ) -> >::Item { entity } @@ -547,7 +549,7 @@ unsafe impl WorldQuery for &T { world .storages() .sparse_sets - .get(state.component_id) + .get(component_id) .unwrap_or_else(|| debug_checked_unreachable()) }), } @@ -567,7 +569,6 @@ unsafe impl WorldQuery for &T { #[inline] unsafe fn set_table<'w>( - &mut self, fetch: &mut ReadFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, @@ -583,11 +584,10 @@ unsafe impl WorldQuery for &T { #[inline(always)] unsafe fn fetch<'w>( - &mut self, - fetch: &mut ReadFetch<'w, T>, + fetch: &mut >::Fetch, entity: Entity, table_row: usize, - ) -> Self::Item { + ) -> >::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => fetch .table_components @@ -641,8 +641,6 @@ impl Clone for ReadFetch<'_, T> { fn clone(&self) -> Self { Self { table_components: self.table_components, - entity_table_rows: self.entity_table_rows, - entities: self.entities, sparse_set: self.sparse_set, } } @@ -700,7 +698,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { world .storages() .sparse_sets - .get(state.component_id) + .get(component_id) .unwrap_or_else(|| debug_checked_unreachable()) }), last_change_tick, @@ -735,11 +733,10 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { #[inline(always)] unsafe fn fetch<'w>( - &mut self, fetch: &mut >::Fetch, entity: Entity, table_row: usize, - ) -> Self::Item { + ) -> >::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { let (table_components, table_ticks) = fetch @@ -809,10 +806,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { impl Clone for WriteFetch<'_, T> { fn clone(&self) -> Self { Self { - table_components: self.table_components, - table_ticks: self.table_ticks, - entities: self.entities, - entity_table_rows: self.entity_table_rows, + table_data: self.table_data, sparse_set: self.sparse_set, last_change_tick: self.last_change_tick, change_tick: self.change_tick, @@ -876,7 +870,7 @@ unsafe impl WorldQuery for Option { ) { fetch.matches = T::matches_component_set(state, &|id| archetype.contains(id)); if fetch.matches { - T::set_archetype(fetch, &state.state, archetype, table); + T::set_archetype(&mut fetch.fetch, state, archetype, table); } } @@ -893,8 +887,10 @@ unsafe impl WorldQuery for Option { fetch: &mut >::Fetch, entity: Entity, table_row: usize, - ) -> Self::Item { - fetch.matches.then(|| T::fetch(fetch, entity, table_row)) + ) -> >::Item { + fetch + .matches + .then(|| T::fetch(&mut fetch.fetch, entity, table_row)) } fn update_component_access(state: &T::State, access: &mut FilteredAccess) { @@ -1045,7 +1041,7 @@ unsafe impl WorldQuery for ChangeTrackers { unsafe fn init_fetch<'w>( world: &'w World, - &id: &ComponentId, + &component_id: &ComponentId, last_change_tick: u32, change_tick: u32, ) -> ChangeTrackersFetch<'w, T> { @@ -1055,7 +1051,7 @@ unsafe impl WorldQuery for ChangeTrackers { world .storages() .sparse_sets - .get(state.component_id) + .get(component_id) .unwrap_or_else(|| debug_checked_unreachable()) }), marker: PhantomData, @@ -1090,7 +1086,7 @@ unsafe impl WorldQuery for ChangeTrackers { fetch: &mut >::Fetch, entity: Entity, table_row: usize, - ) -> Self::Item { + ) -> >::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => ChangeTrackers { component_ticks: { @@ -1196,9 +1192,9 @@ macro_rules! impl_tuple_fetch { _archetype: &'w Archetype, _table: &'w Table ) { - let ($($name,)*) = self; + let ($($name,)*) = _fetch; let ($($state,)*) = _state; - $($name.set_archetype($name, $state, _archetype, _table);)* + $($name::set_archetype($name, $state, _archetype, _table);)* } #[inline] @@ -1211,21 +1207,21 @@ macro_rules! impl_tuple_fetch { #[inline(always)] #[allow(clippy::unused_unit)] unsafe fn fetch<'w>( - _fetch: &mut >::Fetch + _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize - ) -> Self::Item { - let ($($name,)*) = self; + ) -> >::Item { + let ($($name,)*) = _fetch; ($($name::fetch($name, _entity, _table_row),)*) } #[inline(always)] - unsafe fn filter_fetch( - _fetch: &mut >::Fetch + unsafe fn filter_fetch<'w>( + _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize ) -> bool { - let ($($name,)*) = self; + let ($($name,)*) = _fetch; true $(&& $name::filter_fetch($name, _entity, _table_row))* } @@ -1307,7 +1303,7 @@ macro_rules! impl_anytuple_fetch { let ($($name,)*) = _fetch; let ($($state,)*) = _state; $( - $name.1 = $name::matches_component_set($name, $state, &|id| _archetype.contains(id)); + $name.1 = $name::matches_component_set($state, &|id| _archetype.contains(id)); if $name.1 { $name::set_archetype(&mut $name.0, $state, _archetype, _table); } @@ -1328,14 +1324,14 @@ macro_rules! impl_anytuple_fetch { #[inline(always)] #[allow(clippy::unused_unit)] - unsafe fn fetch( + unsafe fn fetch<'w>( _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize - ) -> Self::Item { - let ($($name,)*) = &mut self.0; + ) -> >::Item { + let ($($name,)*) = _fetch; ($( - $name.1.then(|| $name.0.fetch(_entity, _table_row)), + $name.1.then(|| $name::fetch(&mut $name.0, _entity, _table_row)), )*) } @@ -1438,11 +1434,11 @@ unsafe impl WorldQuery for NopWorldQuery { unsafe fn set_table<'w>(_fetch: &mut (), _state: &Q::State, _table: &Table) {} #[inline(always)] - unsafe fn fetch( + unsafe fn fetch<'w>( _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize, - ) -> Self::Item { + ) -> >::Item { } fn update_component_access(_state: &Q::State, _access: &mut FilteredAccess) {} diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 4cadb03a41b6d..b6c20ef7215ef 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -92,7 +92,7 @@ unsafe impl WorldQuery for With { _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize, - ) { + ) -> >::Item { } #[inline] @@ -189,11 +189,11 @@ unsafe impl WorldQuery for Without { } #[inline(always)] - unsafe fn fetch( + unsafe fn fetch<'w>( _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize, - ) { + ) -> >::Item { } #[inline] @@ -327,11 +327,14 @@ macro_rules! impl_query_filter_tuple { } #[inline] - unsafe fn set_archetype<'w>(&mut self, + unsafe fn set_archetype<'w>( fetch: &mut >::Fetch, - state: & Self::State, archetype: &'w Archetype, table: &'w Table) { + state: & Self::State, + archetype: &'w Archetype, + table: &'w Table + ) { let ($($filter,)*) = fetch; - let ($($state,)*) = &state.0; + let ($($state,)*) = &state; $( $filter.matches = $filter::matches_component_set($state, &|id| archetype.contains(id)); if $filter.matches { @@ -341,22 +344,22 @@ macro_rules! impl_query_filter_tuple { } #[inline(always)] - unsafe fn fetch( + unsafe fn fetch<'w>( fetch: &mut >::Fetch, _entity: Entity, _table_row: usize - ) -> Self::Item { - let ($($filter,)*) = &mut self.0; - false $(|| ($filter.matches && $filter.fetch.filter_fetch(_entity, _table_row)))* + ) -> >::Item { + let ($($filter,)*) = fetch; + false $(|| ($filter.matches && $filter::filter_fetch(&mut $filter.fetch, _entity, _table_row)))* } #[inline(always)] - unsafe fn filter_fetch( + unsafe fn filter_fetch<'w>( fetch: &mut >::Fetch, entity: Entity, table_row: usize - ) -> Self::Item { - Self::fetch(fetch, entity, row) + ) -> bool { + Self::fetch(fetch, entity, table_row) } fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { @@ -450,7 +453,7 @@ macro_rules! impl_tick_filter { .then(|| { world.storages() .sparse_sets - .get(state.component_id) + .get(id) .unwrap_or_else(|| debug_checked_unreachable()) }), marker: PhantomData, @@ -471,11 +474,11 @@ macro_rules! impl_tick_filter { #[inline] unsafe fn set_table<'w>( fetch: &mut >::Fetch, - state: &Self::State, + &component_id: &ComponentId, table: &'w Table ) { fetch.table_ticks = Some( - table.get_column(state.component_id) + table.get_column(component_id) .unwrap_or_else(|| debug_checked_unreachable()) .get_ticks_slice() .into() @@ -485,17 +488,21 @@ macro_rules! impl_tick_filter { #[inline] unsafe fn set_archetype<'w>( fetch: &mut >::Fetch, - state: &Self::State, + component_id: &ComponentId, _archetype: &'w Archetype, table: &'w Table ) { if Self::IS_DENSE { - self.set_table(state, table); + Self::set_table(fetch, component_id, table); } } #[inline(always)] - unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Self::Item { + unsafe fn fetch<'w>( + fetch: &mut >::Fetch, + entity: Entity, + table_row: usize + ) -> >::Item { match T::Storage::STORAGE_TYPE { StorageType::Table => { $is_detected(&*( @@ -520,12 +527,12 @@ macro_rules! impl_tick_filter { } #[inline(always)] - unsafe fn filter_fetch(&mut self, - fetch: &mut QueryFetch<'_, Self>, + unsafe fn filter_fetch<'w>( + fetch: &mut QueryFetch<'w, Self>, entity: Entity, table_row: usize - ) -> Self::Item { - self.fetch(entity, table_row) + ) -> bool { + Self::fetch(fetch, entity, table_row) } #[inline] diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index b86b0fa803774..3a59f690f0824 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -139,7 +139,8 @@ where #[inline(always)] unsafe fn fetch_next_aliased_unchecked(&mut self) -> Option> { for entity in self.entity_iter.by_ref() { - let location = match self.entities.get(*entity.borrow()) { + let entity = *entity.borrow(); + let location = match self.entities.get(entity) { Some(location) => location, None => continue, }; @@ -153,6 +154,7 @@ where } let archetype = &self.archetypes[location.archetype_id]; + let table = &self.tables[archetype.table_id()]; // SAFETY: `archetype` is from the world that `fetch/filter` were created for, // `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with @@ -160,7 +162,7 @@ where &mut self.fetch, &self.query_state.fetch_state, archetype, - self.tables, + table, ); // SAFETY: `table` is from the world that `fetch/filter` were created for, // `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with @@ -168,13 +170,15 @@ where &mut self.filter, &self.query_state.filter_state, archetype, - self.tables, + table, ); + + let table_row = archetype.entity_table_row(location.index); // SAFETY: set_archetype was called prior. // `location.index` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d - if F::archetype_filter_fetch(&mut self.filter, location.index) { + if F::filter_fetch(&mut self.filter, entity, table_row) { // SAFETY: set_archetype was called prior, `location.index` is an archetype index in range of the current archetype - return Some(Q::archetype_fetch(&mut self.fetch, location.index)); + return Some(Q::fetch(&mut self.fetch, entity, table_row)); } } None @@ -569,7 +573,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, Some(Q::fetch(&mut self.fetch, *entity, index)) } else { let archetype_entity = self.archetype_entities.get_unchecked(index); - Some(Q::archetype_fetch( + Some(Q::fetch( &mut self.fetch, archetype_entity.entity, archetype_entity.table_row, @@ -619,7 +623,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, // SAFETY: set_table was called prior. // `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed. - let item = Q::table_fetch(&mut self.fetch, *entity, self.current_index); + let item = Q::fetch(&mut self.fetch, *entity, self.current_index); self.current_index += 1; return Some(item); @@ -648,7 +652,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, // SAFETY: set_archetype was called prior. // `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed. let archetype_entity = self.archetype_entities.get_unchecked(self.current_index); - if !F::archetype_filter_fetch( + if !F::filter_fetch( &mut self.filter, archetype_entity.entity, archetype_entity.table_row, @@ -659,7 +663,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, // SAFETY: set_archetype was called prior, `current_index` is an archetype index in range of the current archetype // `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed. - let item = Q::archetype_fetch( + let item = Q::fetch( &mut self.fetch, archetype_entity.entity, archetype_entity.table_row, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 300d6bbfd57d1..67988f1e8cef4 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -413,22 +413,10 @@ impl QueryState { let mut fetch = Q::init_fetch(world, &self.fetch_state, last_change_tick, change_tick); let mut filter = F::init_fetch(world, &self.filter_state, last_change_tick, change_tick); - Q::set_archetype( - &mut fetch, - &self.fetch_state, - archetype, - &world.storages().tables, - ); - F::set_archetype( - &mut filter, - &self.filter_state, - archetype, - &world.storages().tables, - ); - let table = &world.storages().tables[archetype.table_id()]; - fetch.set_archetype(&self.fetch_state, archetype, table); - filter.set_archetype(&self.filter_state, archetype, table); + Q::set_archetype(&mut fetch, &self.fetch_state, archetype, table); + F::set_archetype(&mut filter, &self.filter_state, archetype, table); + if F::filter_fetch(&mut filter, entity, location.index) { Ok(Q::fetch(&mut fetch, entity, location.index)) } else { @@ -1035,7 +1023,7 @@ impl QueryState { if !F::filter_fetch(&mut filter, *entity, row) { continue; } - func(Q::table_fetch(&mut fetch, *entity, row)); + func(Q::fetch(&mut fetch, *entity, row)); } }; #[cfg(feature = "trace")] @@ -1092,7 +1080,7 @@ impl QueryState { ) { continue; } - func(Q::archetype_fetch( + func(Q::fetch( &mut fetch, archetype_entity.entity, archetype_entity.table_row, diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index bb7299195d57f..c184615317063 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -327,7 +327,8 @@ impl World { pub fn iter_entities(&self) -> impl Iterator + '_ { self.archetypes .iter() - .flat_map(|archetype| archetype.entities().iter().copied()) + .flat_map(|archetype| archetype.entities().iter()) + .map(|archetype_entity| archetype_entity.entity) } /// Retrieves an [`EntityMut`] that exposes read and write operations for the given `entity`. diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index e90d34b736955..4df757aa5838f 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -65,7 +65,7 @@ impl Scene { for scene_entity in archetype.entities() { let entity = *instance_info .entity_map - .entry(*scene_entity) + .entry(scene_entity.entity()) .or_insert_with(|| world.spawn_empty().id()); for component_id in archetype.components() { let component_info = self @@ -86,7 +86,7 @@ impl Scene { } }) })?; - reflect_component.copy(&self.world, world, *scene_entity, entity); + reflect_component.copy(&self.world, world, scene_entity.entity(), entity); } } } From 7f794edd45da4168dcb38d94fff019dc6a9e1843 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 15:51:13 -0700 Subject: [PATCH 35/42] Fix CI --- crates/bevy_ecs/macros/src/fetch.rs | 12 ++++++++---- crates/bevy_ecs/src/query/fetch.rs | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs index 861be67b91564..3a339d14c5f0f 100644 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ b/crates/bevy_ecs/macros/src/fetch.rs @@ -266,11 +266,11 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { /// SAFETY: we call `fetch` for each member that implements `Fetch`. #[inline(always)] - unsafe fn fetch<'__w>(&mut self, + unsafe fn fetch<'__w>( _fetch: &mut >::Fetch, _entity: Entity, _table_row: usize - ) -> Self::Item { + ) -> >::Item { Self::Item { #(#field_idents: <#field_types>::fetch(&mut _fetch.#field_idents, _entity, _table_row),)* #(#ignored_field_idents: Default::default(),)* @@ -279,8 +279,12 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream { #[allow(unused_variables)] #[inline(always)] - unsafe fn filter_fetch(&mut self, _entity: Entity, _table_row: usize) -> bool { - true #(&& <#field_types>::filter_fetch(&mut, _fetch.#field_idents, _entity, _table_row))* + unsafe fn filter_fetch<'__w>( + _fetch: &mut >::Fetch, + _entity: Entity, + _table_row: usize + ) -> bool { + true #(&& <#field_types>::filter_fetch(&mut _fetch.#field_idents, _entity, _table_row))* } fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) { diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 230a6e63fecc4..e3570878784ca 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -327,8 +327,8 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { /// Returns true if (and only if) every table of every archetype matched by this fetch contains /// all of the matched components. This is used to select a more efficient "table iterator" - /// for "dense" queries. If this returns true, [`Fetch::set_table`] before [`Fetch::fetch`] - /// will be called for iterators. If this returns false, [`Fetch::set_archetype`] will be used + /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] before [`WorldQuery::fetch`] + /// will be called for iterators. If this returns false, [`WorldQuery::set_archetype`] will be used /// before [`WorldQuery::fetch`] will be called for iterators. const IS_DENSE: bool; @@ -368,12 +368,12 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { /// Fetch [`Self::Item`](`WorldQueryGats::Item`) for either the given `entity` in the current [`Table`], /// or for the given `entity` in the current [`Archetype`]. This must always be called after - /// [`Fetch::set_table`] with a `table_row` in the range of the current [`Table`] or after - /// [`Fetch::set_archetype`] with a `entity` in the current archetype. + /// [`WorldQuery::set_table`] with a `table_row` in the range of the current [`Table`] or after + /// [`WorldQuery::set_archetype`] with a `entity` in the current archetype. /// /// # Safety /// - /// Must always be called _after_ [`Fetch::set_table`] or [`Fetch::set_archetype`]. `entity` and + /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and /// `table_row` must be in the range of the current table and archetype. unsafe fn fetch<'w>( fetch: &mut >::Fetch, @@ -383,12 +383,12 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { /// # Safety /// - /// Must always be called _after_ [`Fetch::set_table`] or [`Fetch::set_archetype`]. `entity` and + /// Must always be called _after_ [`WorldQuery::set_table`] or [`WorldQuery::set_archetype`]. `entity` and /// `table_row` must be in the range of the current table and archetype. #[allow(unused_variables)] #[inline(always)] - unsafe fn filter_fetch<'w>( - fetch: &mut >::Fetch, + unsafe fn filter_fetch( + fetch: &mut >::Fetch, entity: Entity, table_index: usize, ) -> bool { @@ -454,8 +454,8 @@ unsafe impl WorldQuery for Entity { const IS_ARCHETYPAL: bool = true; - unsafe fn init_fetch<'w>( - _world: &'w World, + unsafe fn init_fetch( + _world: &World, _state: &(), _last_change_tick: u32, _change_tick: u32, From 2c31e05fd1f92d5f8e1d65eeb113f92978e3ee43 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 16:00:21 -0700 Subject: [PATCH 36/42] Replace a few remaining unwraps --- crates/bevy_ecs/src/query/fetch.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index e3570878784ca..dead3745a2eeb 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -724,7 +724,9 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { &component_id: &ComponentId, table: &'w Table, ) { - let column = table.get_column(component_id).unwrap(); + let column = table + .get_column(component_id) + .unwrap_or_else(|| debug_checked_unreachable()); fetch.table_data = Some(( column.get_data_slice().into(), column.get_ticks_slice().into(), @@ -1078,7 +1080,13 @@ unsafe impl WorldQuery for ChangeTrackers { &id: &ComponentId, table: &'w Table, ) { - fetch.table_ticks = Some(table.get_column(id).unwrap().get_ticks_slice().into()); + fetch.table_ticks = Some( + table + .get_column(id) + .unwrap_or_else(|| debug_checked_unreachable()) + .get_ticks_slice() + .into(), + ); } #[inline(always)] From 24b0b99e9685cf46751d72d7a881f4ae5d3aaa4e Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 18:15:13 -0700 Subject: [PATCH 37/42] Remove EntityFetch --- crates/bevy_ecs/src/query/fetch.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index dead3745a2eeb..6285e6ed3cccf 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -437,10 +437,6 @@ pub type ROQueryFetch<'w, Q> = QueryFetch<'w, ::ReadOnly>; /// The read-only variant of the item type returned when a [`WorldQuery`] is iterated over immutably pub type ROQueryItem<'w, Q> = QueryItem<'w, ::ReadOnly>; -#[doc(hidden)] -#[derive(Clone)] -pub struct EntityFetch; - /// SAFETY: no component or archetype access unsafe impl WorldQuery for Entity { type ReadOnly = Self; @@ -454,30 +450,34 @@ unsafe impl WorldQuery for Entity { const IS_ARCHETYPAL: bool = true; - unsafe fn init_fetch( - _world: &World, - _state: &(), + unsafe fn init_fetch<'w>( + _world: &'w World, + _state: &Self::State, _last_change_tick: u32, _change_tick: u32, - ) -> EntityFetch { - EntityFetch + ) -> >::Fetch { } #[inline] unsafe fn set_archetype<'w>( - _fetch: &mut EntityFetch, - _state: &(), + _fetch: &mut >::Fetch, + _state: &Self::State, _archetype: &'w Archetype, _table: &Table, ) { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut EntityFetch, _state: &(), _table: &'w Table) {} + unsafe fn set_table<'w>( + _fetch: &mut >::Fetch, + _state: &Self::State, + _table: &'w Table, + ) { + } #[inline(always)] unsafe fn fetch<'w>( - _fetch: &mut EntityFetch, + _fetch: &mut >::Fetch, entity: Entity, _table_row: usize, ) -> >::Item { @@ -504,7 +504,7 @@ unsafe impl WorldQuery for Entity { } impl<'w> WorldQueryGats<'w> for Entity { - type Fetch = EntityFetch; + type Fetch = (); type Item = Entity; } From 2832c0456cd09967949958256868420e1d685acd Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 18:16:32 -0700 Subject: [PATCH 38/42] Fix up the doc comment --- crates/bevy_ecs/src/query/fetch.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 6285e6ed3cccf..cf44b4af2a192 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -327,9 +327,10 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { /// Returns true if (and only if) every table of every archetype matched by this fetch contains /// all of the matched components. This is used to select a more efficient "table iterator" - /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] before [`WorldQuery::fetch`] - /// will be called for iterators. If this returns false, [`WorldQuery::set_archetype`] will be used - /// before [`WorldQuery::fetch`] will be called for iterators. + /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before + /// [`WorldQuery::fetch`] can be called for iterators. If this returns false, + /// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for + /// iterators. const IS_DENSE: bool; /// Returns true if (and only if) this Fetch relies strictly on archetypes to limit which From 659f644b0d26fb64d21d040e94648589558dd899 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 18:19:01 -0700 Subject: [PATCH 39/42] Fix rebase error --- crates/bevy_ecs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 5b9b6520766ee..197e136894312 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -541,7 +541,7 @@ mod tests { .spawn((TableStored("def"), A(456), SparseStored(1))) .id(); // this should be skipped - // SparseStored(1).spawn().insert("abc"); + // world.spawn(SparseStored(1)); let ents = world .query::<(Entity, Option<&SparseStored>, &A)>() .iter(&world) From 92cc6e1973e4428721cb2992aaba449639f85dd7 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 24 Oct 2022 18:24:53 -0700 Subject: [PATCH 40/42] Fix up the doc comment --- crates/bevy_ecs/src/query/fetch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index cf44b4af2a192..a01c4994df15a 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -327,9 +327,9 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { /// Returns true if (and only if) every table of every archetype matched by this fetch contains /// all of the matched components. This is used to select a more efficient "table iterator" - /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before - /// [`WorldQuery::fetch`] can be called for iterators. If this returns false, - /// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for + /// for "dense" queries. If this returns true, [`WorldQuery::set_table`] must be used before + /// [`WorldQuery::fetch`] can be called for iterators. If this returns false, + /// [`WorldQuery::set_archetype`] must be used before [`WorldQuery::fetch`] can be called for /// iterators. const IS_DENSE: bool; From 47ad0d1bfccde54726b30d65efedb4f5a4dbc822 Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 27 Oct 2022 23:31:01 -0700 Subject: [PATCH 41/42] Fix CI --- crates/bevy_ecs/src/query/fetch.rs | 14 ++------------ crates/bevy_ecs/src/query/filter.rs | 2 -- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 73c065068e5f4..aaf13bca69964 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -470,11 +470,8 @@ unsafe impl WorldQuery for Entity { } unsafe fn clone_fetch<'w>( - fetch: &>::Fetch, + _fetch: &>::Fetch, ) -> >::Fetch { - EntityFetch { - entities: fetch.entities, - } } #[inline] @@ -579,8 +576,6 @@ unsafe impl WorldQuery for &T { ) -> >::Fetch { ReadFetch { table_components: fetch.table_components, - entity_table_rows: fetch.entity_table_rows, - entities: fetch.entities, sparse_set: fetch.sparse_set, } } @@ -731,10 +726,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { fetch: &>::Fetch, ) -> >::Fetch { WriteFetch { - table_components: fetch.table_components, - table_ticks: fetch.table_ticks, - entities: fetch.entities, - entity_table_rows: fetch.entity_table_rows, + table_data: fetch.table_data, sparse_set: fetch.sparse_set, last_change_tick: fetch.last_change_tick, change_tick: fetch.change_tick, @@ -1088,8 +1080,6 @@ unsafe impl WorldQuery for ChangeTrackers { ) -> >::Fetch { ChangeTrackersFetch { table_ticks: fetch.table_ticks, - entity_table_rows: fetch.entity_table_rows, - entities: fetch.entities, sparse_set: fetch.sparse_set, marker: fetch.marker, last_change_tick: fetch.last_change_tick, diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 4787cbd6a5685..1571907f567a6 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -476,8 +476,6 @@ macro_rules! impl_tick_filter { ) -> >::Fetch { $fetch_name { table_ticks: fetch.table_ticks, - entity_table_rows: fetch.entity_table_rows, - entities: fetch.entities, sparse_set: fetch.sparse_set, last_change_tick: fetch.last_change_tick, change_tick: fetch.change_tick, From 8f9f56853d81a16f2025a305fedddcd0ad0310ad Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 27 Oct 2022 23:36:07 -0700 Subject: [PATCH 42/42] Address review comments --- crates/bevy_ecs/src/query/fetch.rs | 4 ++-- crates/bevy_ecs/src/query/iter.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index aaf13bca69964..52886817e20e0 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -389,7 +389,7 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { unsafe fn fetch<'w>( fetch: &mut >::Fetch, entity: Entity, - table_index: usize, + table_row: usize, ) -> >::Item; /// # Safety @@ -401,7 +401,7 @@ pub unsafe trait WorldQuery: for<'w> WorldQueryGats<'w> { unsafe fn filter_fetch( fetch: &mut >::Fetch, entity: Entity, - table_index: usize, + table_row: usize, ) -> bool { true } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index af7baa6f6bbbb..8a693eabb02c4 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -466,7 +466,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery, const K: usize> Fused struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, - entities: &'w [Entity], + table_entities: &'w [Entity], archetype_entities: &'w [ArchetypeEntity], fetch: QueryFetch<'w, Q>, filter: QueryFetch<'w, F>, @@ -488,7 +488,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, Self { table_id_iter: self.table_id_iter.clone(), archetype_id_iter: self.archetype_id_iter.clone(), - entities: self.entities, + table_entities: self.table_entities, archetype_entities: self.archetype_entities, // SAFETY: upheld by caller invariants fetch: Q::clone_fetch(&self.fetch), @@ -537,7 +537,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, QueryIterationCursor { fetch, filter, - entities: &[], + table_entities: &[], archetype_entities: &[], table_id_iter: query_state.matched_table_ids.iter(), archetype_id_iter: query_state.matched_archetype_ids.iter(), @@ -553,7 +553,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, if self.current_index > 0 { let index = self.current_index - 1; if Self::IS_DENSE { - let entity = self.entities.get_unchecked(index); + let entity = self.table_entities.get_unchecked(index); Some(Q::fetch(&mut self.fetch, *entity, index)) } else { let archetype_entity = self.archetype_entities.get_unchecked(index); @@ -591,7 +591,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, // `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with Q::set_table(&mut self.fetch, &query_state.fetch_state, table); F::set_table(&mut self.filter, &query_state.filter_state, table); - self.entities = table.entities(); + self.table_entities = table.entities(); self.current_len = table.entity_count(); self.current_index = 0; continue; @@ -599,7 +599,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's, // SAFETY: set_table was called prior. // `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed. - let entity = self.entities.get_unchecked(self.current_index); + let entity = self.table_entities.get_unchecked(self.current_index); if !F::filter_fetch(&mut self.filter, *entity, self.current_index) { self.current_index += 1; continue;