From 193cc01e8730be0c1fa2190222a01ecd93c47714 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 6 Apr 2025 11:40:43 +0000 Subject: [PATCH 01/16] pass `TableId` to `set_table` --- crates/bevy_asset/src/asset_changed.rs | 10 +- crates/bevy_ecs/macros/src/world_query.rs | 5 +- crates/bevy_ecs/src/query/fetch.rs | 115 +++++++++++++++++----- crates/bevy_ecs/src/query/filter.rs | 22 +++-- crates/bevy_ecs/src/query/iter.rs | 18 +++- crates/bevy_ecs/src/query/mod.rs | 3 +- crates/bevy_ecs/src/query/world_query.rs | 14 ++- crates/bevy_render/src/sync_world.rs | 10 +- 8 files changed, 146 insertions(+), 51 deletions(-) diff --git a/crates/bevy_asset/src/asset_changed.rs b/crates/bevy_asset/src/asset_changed.rs index f8501cb206f34..a998ccc67310f 100644 --- a/crates/bevy_asset/src/asset_changed.rs +++ b/crates/bevy_asset/src/asset_changed.rs @@ -5,6 +5,7 @@ use crate::{AsAssetId, Asset, AssetId}; use bevy_ecs::component::Components; +use bevy_ecs::storage::TableId; use bevy_ecs::{ archetype::Archetype, component::{ComponentId, Tick}, @@ -216,11 +217,16 @@ unsafe impl WorldQuery for AssetChanged { } } - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + table: &'w Table, + table_id: TableId, + ) { if let Some(inner) = &mut fetch.inner { // SAFETY: We delegate to the inner `set_table` for `A` unsafe { - <&A>::set_table(inner, &state.asset_id, table); + <&A>::set_table(inner, &state.asset_id, table, table_id); } } } diff --git a/crates/bevy_ecs/macros/src/world_query.rs b/crates/bevy_ecs/macros/src/world_query.rs index 77ee532a505f7..b79e02c7ed9e4 100644 --- a/crates/bevy_ecs/macros/src/world_query.rs +++ b/crates/bevy_ecs/macros/src/world_query.rs @@ -147,9 +147,10 @@ pub(crate) fn world_query_impl( unsafe fn set_table<'__w>( _fetch: &mut ::Fetch<'__w>, _state: &Self::State, - _table: &'__w #path::storage::Table + _table: &'__w #path::storage::Table, + _table_id: #path::storage::TableId ) { - #(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)* + #(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table, _table_id);)* } 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 cd632f7b14f22..bd831e97fdf8e 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -5,7 +5,7 @@ use crate::{ component::{Component, ComponentId, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, - storage::{ComponentSparseSet, Table, TableRow}, + storage::{ComponentSparseSet, Table, TableId, TableRow}, world::{ unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, @@ -350,7 +350,12 @@ unsafe impl WorldQuery for Entity { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -427,7 +432,12 @@ unsafe impl WorldQuery for EntityLocation { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -503,7 +513,12 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { @@ -584,7 +599,12 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { @@ -665,7 +685,12 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + _: &'w Table, + _table_id: TableId, + ) { fetch.1.clone_from(&state.access); } @@ -761,7 +786,12 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + _: &'w Table, + _table_id: TableId, + ) { fetch.1.clone_from(&state.access); } @@ -853,7 +883,7 @@ where ) { } - unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} + unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table, _: TableId) {} fn update_component_access( state: &Self::State, @@ -953,7 +983,7 @@ where ) { } - unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} + unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table, _: TableId) {} fn update_component_access( state: &Self::State, @@ -1051,7 +1081,12 @@ unsafe impl WorldQuery for &Archetype { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } fn update_component_access(_state: &Self::State, _access: &mut FilteredAccess) {} @@ -1160,13 +1195,14 @@ unsafe impl WorldQuery for &T { unsafe fn set_archetype<'w>( fetch: &mut ReadFetch<'w, T>, component_id: &ComponentId, - _archetype: &'w Archetype, + archetype: &'w Archetype, table: &'w Table, ) { if Self::IS_DENSE { + let table_id = archetype.table_id(); // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { - Self::set_table(fetch, component_id, table); + Self::set_table(fetch, component_id, table, table_id); } } } @@ -1176,6 +1212,7 @@ unsafe impl WorldQuery for &T { fetch: &mut ReadFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, + table_id: TableId, ) { let table_data = Some( table @@ -1329,13 +1366,13 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { unsafe fn set_archetype<'w>( fetch: &mut RefFetch<'w, T>, component_id: &ComponentId, - _archetype: &'w Archetype, + archetype: &'w Archetype, table: &'w Table, ) { if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { - Self::set_table(fetch, component_id, table); + Self::set_table(fetch, component_id, table, archetype.table_id()); } } } @@ -1345,6 +1382,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { fetch: &mut RefFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, + _table_id: TableId, ) { let column = table.get_column(component_id).debug_checked_unwrap(); let table_data = Some(( @@ -1524,13 +1562,13 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe fn set_archetype<'w>( fetch: &mut WriteFetch<'w, T>, component_id: &ComponentId, - _archetype: &'w Archetype, + archetype: &'w Archetype, table: &'w Table, ) { if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { - Self::set_table(fetch, component_id, table); + Self::set_table(fetch, component_id, table, archetype.table_id()); } } } @@ -1540,6 +1578,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { fetch: &mut WriteFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, + _table_id: TableId, ) { let column = table.get_column(component_id).debug_checked_unwrap(); let table_data = Some(( @@ -1687,8 +1726,13 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { #[inline] // Forwarded to `&mut T` - unsafe fn set_table<'w>(fetch: &mut WriteFetch<'w, T>, state: &ComponentId, table: &'w Table) { - <&mut T as WorldQuery>::set_table(fetch, state, table); + unsafe fn set_table<'w>( + fetch: &mut WriteFetch<'w, T>, + state: &ComponentId, + table: &'w Table, + table_id: TableId, + ) { + <&mut T as WorldQuery>::set_table(fetch, state, table, table_id); } // NOT forwarded to `&mut T` @@ -1812,12 +1856,17 @@ unsafe impl WorldQuery for Option { } #[inline] - unsafe fn set_table<'w>(fetch: &mut OptionFetch<'w, T>, state: &T::State, table: &'w Table) { + unsafe fn set_table<'w>( + fetch: &mut OptionFetch<'w, T>, + state: &T::State, + table: &'w Table, + table_id: TableId, + ) { fetch.matches = T::matches_component_set(state, &|id| table.has_column(id)); if fetch.matches { // SAFETY: The invariants are upheld by the caller. unsafe { - T::set_table(&mut fetch.fetch, state, table); + T::set_table(&mut fetch.fetch, state, table, table_id); } } } @@ -1989,7 +2038,12 @@ unsafe impl WorldQuery for Has { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + table: &'w Table, + _table_id: TableId, + ) { *fetch = table.has_column(*state); } @@ -2161,14 +2215,14 @@ macro_rules! impl_anytuple_fetch { } #[inline] - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table, _table_id: TableId) { let ($($name,)*) = _fetch; let ($($state,)*) = _state; $( $name.1 = $name::matches_component_set($state, &|id| _table.has_column(id)); if $name.1 { // SAFETY: The invariants are required to be upheld by the caller. - unsafe { $name::set_table(&mut $name.0, $state, _table); } + unsafe { $name::set_table(&mut $name.0, $state, _table, _table_id); } } )* } @@ -2314,7 +2368,13 @@ unsafe impl WorldQuery for NopWorldQuery { } #[inline(always)] - unsafe fn set_table<'w>(_fetch: &mut (), _state: &D::State, _table: &Table) {} + unsafe fn set_table<'w>( + _fetch: &mut (), + _state: &D::State, + _table: &Table, + _table_id: TableId, + ) { + } fn update_component_access(_state: &D::State, _access: &mut FilteredAccess) {} @@ -2385,7 +2445,12 @@ unsafe impl WorldQuery for PhantomData { ) { } - unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + unsafe fn set_table<'w>( + _fetch: &mut Self::Fetch<'w>, + _state: &Self::State, + _table: &'w Table, + _table_id: TableId, + ) { } 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 e4e1f0fd668d3..16cde14d886f7 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -3,7 +3,7 @@ use crate::{ component::{Component, ComponentId, Components, StorageType, Tick}, entity::Entity, query::{DebugCheckedUnwrap, FilteredAccess, StorageSwitch, WorldQuery}, - storage::{ComponentSparseSet, Table, TableRow}, + storage::{ComponentSparseSet, Table, TableId, TableRow}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; @@ -173,7 +173,8 @@ unsafe impl WorldQuery for With { } #[inline] - unsafe fn set_table(_fetch: &mut (), _state: &ComponentId, _table: &Table) {} + unsafe fn set_table(_fetch: &mut (), _state: &ComponentId, _table: &Table, _table_id: TableId) { + } #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { @@ -273,7 +274,8 @@ unsafe impl WorldQuery for Without { } #[inline] - unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &Table) {} + unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &Table, _table_id: TableId) { + } #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { @@ -408,14 +410,14 @@ macro_rules! impl_or_query_filter { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table, table_id: TableId) { let ($($filter,)*) = fetch; let ($($state,)*) = state; $( $filter.matches = $filter::matches_component_set($state, &|id| table.has_column(id)); if $filter.matches { // SAFETY: The invariants are upheld by the caller. - unsafe { $filter::set_table(&mut $filter.fetch, $state, table); } + unsafe { $filter::set_table(&mut $filter.fetch, $state, table, table_id); } } )* } @@ -692,13 +694,13 @@ unsafe impl WorldQuery for Added { unsafe fn set_archetype<'w>( fetch: &mut Self::Fetch<'w>, component_id: &ComponentId, - _archetype: &'w Archetype, + archetype: &'w Archetype, table: &'w Table, ) { if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { - Self::set_table(fetch, component_id, table); + Self::set_table(fetch, component_id, table, archetype.table_id()); } } } @@ -708,6 +710,7 @@ unsafe impl WorldQuery for Added { fetch: &mut Self::Fetch<'w>, &component_id: &ComponentId, table: &'w Table, + _table_id: TableId, ) { let table_ticks = Some( table @@ -919,13 +922,13 @@ unsafe impl WorldQuery for Changed { unsafe fn set_archetype<'w>( fetch: &mut Self::Fetch<'w>, component_id: &ComponentId, - _archetype: &'w Archetype, + archetype: &'w Archetype, table: &'w Table, ) { if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { - Self::set_table(fetch, component_id, table); + Self::set_table(fetch, component_id, table, archetype.table_id()); } } } @@ -935,6 +938,7 @@ unsafe impl WorldQuery for Changed { fetch: &mut Self::Fetch<'w>, &component_id: &ComponentId, table: &'w Table, + _table_id: TableId, ) { let table_ticks = Some( table diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index fc89843493a03..abd9b19b00449 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -5,7 +5,7 @@ use crate::{ component::Tick, entity::{ContainsEntity, Entities, Entity, EntityEquivalent, EntitySet, EntitySetIterator}, query::{ArchetypeFilter, DebugCheckedUnwrap, QueryState, StorageId}, - storage::{Table, TableRow, Tables}, + storage::{Table, TableId, TableRow, Tables}, world::{ unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, FilteredEntityMut, FilteredEntityRef, @@ -153,7 +153,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { // - The fetched table matches both D and F // - caller ensures `range` is within `[0, table.entity_count)` // - The if block ensures that the query iteration is dense - unsafe { self.fold_over_table_range(accum, func, table, range) }; + unsafe { self.fold_over_table_range(accum, func, table, table_id, range) }; } else { // SAFETY: `self.cursor.is_dense` is false, so storage ids are guaranteed to be archetype ids. let archetype_id = unsafe { storage.archetype_id }; @@ -192,6 +192,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// # Safety /// - all `rows` must be in `[0, table.entity_count)`. /// - `table` must match D and F + /// - `table_id` must match `table` /// - The query iteration must be dense (i.e. `self.query_state.is_dense` must be true). #[inline] pub(super) unsafe fn fold_over_table_range( @@ -199,6 +200,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { mut accum: B, func: &mut Func, table: &'w Table, + table_id: TableId, rows: Range, ) -> B where @@ -212,11 +214,17 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { "TableRow is only valid up to u32::MAX" ); - D::set_table(&mut self.cursor.fetch, &self.query_state.fetch_state, table); + D::set_table( + &mut self.cursor.fetch, + &self.query_state.fetch_state, + table, + table_id, + ); F::set_table( &mut self.cursor.filter, &self.query_state.filter_state, table, + table_id, ); let entities = table.entities(); @@ -2473,8 +2481,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> { // 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 unsafe { - D::set_table(&mut self.fetch, &query_state.fetch_state, table); - F::set_table(&mut self.filter, &query_state.filter_state, table); + D::set_table(&mut self.fetch, &query_state.fetch_state, table, table_id); + F::set_table(&mut self.filter, &query_state.filter_state, table, table_id); } self.table_entities = table.entities(); self.current_len = table.entity_count(); diff --git a/crates/bevy_ecs/src/query/mod.rs b/crates/bevy_ecs/src/query/mod.rs index c1744cbf24211..54cde32bb711a 100644 --- a/crates/bevy_ecs/src/query/mod.rs +++ b/crates/bevy_ecs/src/query/mod.rs @@ -113,7 +113,7 @@ mod tests { ReadOnlyQueryData, WorldQuery, }, schedule::{IntoScheduleConfigs, Schedule}, - storage::{Table, TableRow}, + storage::{Table, TableId, TableRow}, system::{assert_is_system, IntoSystem, Query, System, SystemState}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -849,6 +849,7 @@ mod tests { _fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table, + _table_id: TableId, ) { } diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index da147770e0fcf..be62165728a47 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -2,7 +2,7 @@ use crate::{ archetype::Archetype, component::{ComponentId, Components, Tick}, query::FilteredAccess, - storage::Table, + storage::{Table, TableId}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use variadics_please::all_tuples; @@ -98,8 +98,14 @@ pub unsafe trait WorldQuery { /// # Safety /// /// - `table` must be from the same [`World`] that [`WorldQuery::init_state`] was called on. + /// - `table_id` must match `table`. /// - `state` must be the [`State`](Self::State) that `fetch` was initialized with. - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table); + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + table: &'w Table, + table_id: TableId, + ); /// Sets available accesses for implementors with dynamic access such as [`FilteredEntityRef`](crate::world::FilteredEntityRef) /// or [`FilteredEntityMut`](crate::world::FilteredEntityMut). @@ -192,11 +198,11 @@ macro_rules! impl_tuple_world_query { } #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { + unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table, table_id: TableId) { let ($($name,)*) = fetch; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. - $(unsafe { $name::set_table($name, $state, table); })* + $(unsafe { $name::set_table($name, $state, table, table_id); })* } diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 7bb74c5cab3b8..224eb122f6b0a 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -278,7 +278,7 @@ mod render_entities_world_query_impls { component::{ComponentId, Components, Tick}, entity::Entity, query::{FilteredAccess, QueryData, ReadOnlyQueryData, WorldQuery}, - storage::{Table, TableRow}, + storage::{Table, TableId, TableRow}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -327,9 +327,12 @@ mod render_entities_world_query_impls { fetch: &mut Self::Fetch<'w>, &component_id: &ComponentId, table: &'w Table, + table_id: TableId, ) { // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`. - unsafe { <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table) } + unsafe { + <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table, table_id) + } } fn update_component_access( @@ -427,9 +430,10 @@ mod render_entities_world_query_impls { fetch: &mut Self::Fetch<'w>, &component_id: &ComponentId, table: &'w Table, + table_id: TableId, ) { // SAFETY: defers to the `&T` implementation, with T set to `MainEntity`. - unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table) } + unsafe { <&MainEntity as WorldQuery>::set_table(fetch, &component_id, table, table_id) } } fn update_component_access( From ace3ef53c1c759451003dc85741ef72bf80f5fbe Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:42:47 +0000 Subject: [PATCH 02/16] add base inherited components implementation --- crates/bevy_ecs/src/component.rs | 2 +- crates/bevy_ecs/src/inheritance.rs | 719 +++++++++++++++++++++++++++++ crates/bevy_ecs/src/lib.rs | 1 + 3 files changed, 721 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_ecs/src/inheritance.rs diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 51a10a51b14fd..ec4ff38fde158 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -2641,7 +2641,7 @@ impl<'a> TickCells<'a> { } /// Records when a component or resource was added and when it was last mutably dereferenced (or added). -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))] pub struct ComponentTicks { /// Tick recording the time this component or resource was added. diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs new file mode 100644 index 0000000000000..bda8ae9aa4f86 --- /dev/null +++ b/crates/bevy_ecs/src/inheritance.rs @@ -0,0 +1,719 @@ +//! This module contains code relating to component inheritance. +//! +//! [`InheritedComponents`] is the main structure holding data about inherited components, it can be used +//! to record and resolve archetypes/tables that contain components from an entity in another archetype/table. +//! +//! [`InheritFrom`] is the main user-facing component that allows some entity to inherit components from some other entity. +use alloc::collections::vec_deque::VecDeque; +use alloc::string::ToString; +use alloc::vec::Vec; +use bevy_platform_support::collections::{HashMap, HashSet}; +use core::{alloc::Layout, ptr::NonNull}; + +use bevy_ptr::OwningPtr; + +use crate::{ + archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, + component::{ + Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, Components, + HookContext, StorageType, + }, + entity::{Entities, Entity, EntityHashMap}, + storage::TableId, + world::{DeferredWorld, World}, +}; + +#[derive(Component)] +#[component( + immutable, + on_insert=InheritFrom::on_insert, + on_remove=InheritFrom::on_remove_or_replace, + on_replace=InheritFrom::on_remove_or_replace, +)] +/// Mark this entity to inherit components from the provided entity. +/// Multiple levels of single inheritance are supported, but each entity can inherit component from only one other entity. +/// +/// At the moment, components are inherited only read-only, trying to access inherited component as mutable will behave as if +/// inherited component doesn't exist. +/// +/// Circular inheritance is not supported and creating cycles will result in unpredictably-inherited components. +pub struct InheritFrom(pub Entity); + +impl InheritFrom { + fn on_insert(mut world: DeferredWorld, ctx: HookContext) { + let entity = ctx.entity; + let base = world.entity(entity).get::().unwrap().0; + world.commands().queue(move |world: &mut World| { + let base_component_id = + if let Some(id) = world.inherited_components.entities_to_ids.get(&base) { + *id + } else { + let name = [base.to_string(), "-base".to_string()].join(""); + let id = + // SAFETY: The component descriptor is for a ZST, so it's Send + Sync and drop is None + unsafe { + world.register_component_with_descriptor(ComponentDescriptor::new_with_layout( + name, + StorageType::Table, + Layout::new::<()>(), + None, + false, + ComponentCloneBehavior::Ignore, + )) + }; + world.flush_components(); + world.inherited_components.ids_to_entities.insert(id, base); + world.inherited_components.entities_to_ids.insert(base, id); + id + }; + // SAFETY: + // - NonNull::dangling is a valid data pointer for a ZST component. + // - Component id is from the same world as entity. + unsafe { + world + .entity_mut(entity) + .insert_by_id(base_component_id, OwningPtr::new(NonNull::dangling())); + } + world.entity_mut(base).insert(Inherited); + }); + } + fn on_remove_or_replace(mut world: DeferredWorld, ctx: HookContext) { + let entity = ctx.entity; + let base = world.entity(entity).get::().unwrap().0; + world.commands().queue(move |world: &mut World| { + if let Some(&base_component_id) = world.inherited_components.entities_to_ids.get(&base) + { + if let Ok(mut entity) = world.get_entity_mut(entity) { + entity.remove_by_id(base_component_id); + } + } + }); + } +} + +#[derive(Component)] +#[component( + immutable, + storage = "SparseSet", + on_remove=Inherited::on_remove, +)] +/// A marker component that indicates that this entity is inherited by other entities. +pub struct Inherited; + +impl Inherited { + fn on_remove(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) { + world.commands().queue(move |world: &mut World| { + let Some(component_id) = world.inherited_components.entities_to_ids.remove(&entity) + else { + return; + }; + world + .inherited_components + .ids_to_entities + .remove(&component_id); + let Some(entities) = + world + .archetypes() + .component_index() + .get(&component_id) + .map(|map| { + map.keys() + .flat_map(|archetype_id| { + world + .archetypes() + .get(*archetype_id) + .unwrap() + .entities() + .iter() + .map(ArchetypeEntity::id) + }) + .collect::>() + }) + else { + return; + }; + for entity in entities { + world.entity_mut(entity).remove_by_id(component_id); + } + }); + } +} + +#[derive(Default)] +/// Contains information about inherited components and entities. +/// +/// If an archetype or a table has inherited components, this will contain +/// all the data required to get which components are inherited and to get the actual component data. +pub struct InheritedComponents { + /// Mapping of entities to component ids representing the inherited archetypes and tables. + /// Must be kept synchronized with `ids_to_entities` + pub(crate) entities_to_ids: EntityHashMap, + /// Mapping of component ids to entities representing the inherited archetypes and tables. + /// Must be kept synchronized with `entities_to_ids` + pub(crate) ids_to_entities: HashMap, + /// List of inherited components along with entities containing the inherited component for the archetypes + pub(crate) archetype_inherited_components: + HashMap>, + /// List of inherited components along with entities containing the inherited component for the tables + pub(crate) table_inherited_components: HashMap>, +} + +impl InheritedComponents { + /// This method must be called after a new archetype is created to initialized inherited components once. + pub(crate) fn init_inherited_components( + &mut self, + entities: &Entities, + components: &Components, + archetypes: &mut Archetypes, + archetype_id: ArchetypeId, + ) { + let archetype = &archetypes.archetypes[archetype_id.index()]; + if !archetype.has_inherited_components() { + return; + } + // Empty inherited components hashmap to enable .chain + let empty_inherited_components = HashMap::default(); + let archetype_inherited_components = HashMap::from_iter( + archetype + .table_components() + .filter_map(|id| { + self.ids_to_entities + .get(&id) + .and_then(|entity| { + entities.get(*entity).map(|location| (location, *entity)) + }) + .and_then(|(location, entity)| { + archetypes + .get(location.archetype_id) + .map(|archetype| (archetype, entity)) + }) + }) + .flat_map(|(inherited_archetype, entity)| { + inherited_archetype + .components_with_archetype_component_id() + .zip(core::iter::repeat(entity)) + .map(|((component_id, archetype_component_id), entity)| { + (component_id, (entity, archetype_component_id)) + }) + .chain( + self.archetype_inherited_components + .get(&inherited_archetype.id()) + .unwrap_or(&empty_inherited_components) + .iter() + .map(|(&id, &entity)| (id, entity)), + ) + }) + .filter(|(id, ..)| !archetype.contains(*id)), + ); + + // Update component index to include this archetype in all the inherited components. + for (inherited_component, ..) in archetype_inherited_components.iter() { + archetypes + .by_component + .entry(*inherited_component) + .or_default() + .insert( + archetype_id, + ArchetypeRecord { + column: None, + is_inherited: true, + }, + ); + } + + // If table already contains inherited components, it is already initialized. + // Since the only difference between archetypes with the same table is in sparse set components, + // we can skip reinitializing table's inherited components. + // `update_inherited_components` will take care of updating existing table's components. + self.table_inherited_components + .entry(archetype.table_id()) + .or_insert_with(|| { + HashMap::from_iter( + archetype_inherited_components + .iter() + .map(|(id, (e, ..))| (*id, *e)) + .filter(|(id, ..)| { + components.get_info(*id).unwrap().storage_type() == StorageType::Table + }), + ) + }); + + self.archetype_inherited_components + .insert(archetype_id, archetype_inherited_components); + } + + /// This method must be called after an entity changes archetype to update all archetypes inheriting components from this entity. + pub(crate) fn update_inherited_archetypes( + &mut self, + archetypes: &mut Archetypes, + components: &Components, + old_base_archetype_id: ArchetypeId, + new_base_archetype_id: ArchetypeId, + entity: Entity, + ) { + let Some(component_id) = self.entities_to_ids.get(&entity) else { + return; + }; + let old_base_archetype = &archetypes.archetypes[old_base_archetype_id.index()]; + let new_base_archetype = &archetypes.archetypes[new_base_archetype_id.index()]; + let added_components = new_base_archetype + .components_with_archetype_component_id() + .filter(|(component_id, ..)| !old_base_archetype.contains(*component_id)) + .collect::>(); + let mut removed_components = old_base_archetype + .components() + .filter(|component_id| !new_base_archetype.contains(*component_id)) + .collect::>(); + removed_components.sort_unstable(); + + let mut inherited_entities_queue = VecDeque::from([(entity, *component_id)]); + let mut archetypes_to_update = Vec::new(); + let mut processed_archetypes = HashSet::::default(); + + while let Some((entity, component_id)) = inherited_entities_queue.pop_front() { + archetypes_to_update.extend( + archetypes + .by_component + .get(&component_id) + .unwrap() + .keys() + .copied() + .filter(|archetype_id| !processed_archetypes.contains(archetype_id)), + ); + for archetype in archetypes_to_update.drain(..) { + // Update archetype's inherited components + let archetype = &archetypes.archetypes[archetype.index()]; + let archetype_inherited_components = self + .archetype_inherited_components + .entry(archetype.id()) + .or_default(); + archetype_inherited_components.retain(|component_id, _| { + removed_components.binary_search(component_id).is_err() + }); + archetype_inherited_components.extend( + added_components + .iter() + .copied() + .filter(|(component_id, ..)| !archetype.contains(*component_id)) + .zip(core::iter::repeat(entity)) + .map(|((component_id, archetype_component_id), entity)| { + (component_id, (entity, archetype_component_id)) + }), + ); + + // Update component index + for removed_component in &removed_components { + archetypes + .by_component + .entry(*removed_component) + .and_modify(|map| { + if let Some(record) = map.get(&archetype.id()) { + if !record.is_inherited { + return; + } + } + map.remove(&archetype.id()); + }); + } + for (added_component, ..) in &added_components { + archetypes + .by_component + .entry(*added_component) + .and_modify(|map| { + if !map.contains_key(&archetype.id()) { + map.insert( + archetype.id(), + ArchetypeRecord { + column: None, + is_inherited: true, + }, + ); + } + }); + } + + // Update tables's inherited components + if UPDATE_TABLES { + let table_inherited_components = self + .table_inherited_components + .entry(archetype.table_id()) + .or_default(); + table_inherited_components.retain(|component_id, _| { + removed_components.binary_search(component_id).is_err() + }); + table_inherited_components.extend( + added_components + .iter() + .map(|(component_id, ..)| *component_id) + .filter(|component_id| { + !archetype.contains(*component_id) + && components.get_info(*component_id).unwrap().storage_type() + == StorageType::Table + }) + .zip(core::iter::repeat(entity)), + ); + } + + if archetype.is_inherited() { + for (entity, component_id) in archetype.entities().iter().filter_map(|entity| { + self.entities_to_ids + .get(&entity.id()) + .map(|component_id| (entity.id(), *component_id)) + }) { + inherited_entities_queue.push_back((entity, component_id)); + } + processed_archetypes.insert(archetype.id()); + } + } + } + } + + /// Returns an iterator yielding all archetypes acting as a base for the passed archetype. + pub fn get_base_archetypes( + &self, + entities: &Entities, + archetype_id: ArchetypeId, + ) -> impl Iterator { + HashSet::::from_iter( + self.archetype_inherited_components + .get(&archetype_id) + .unwrap() + .values() + .filter_map(|(entity, ..)| { + entities.get(*entity).map(|location| location.archetype_id) + }), + ) + .into_iter() + } +} + +#[cfg(test)] +mod tests { + use crate as bevy_ecs; + use crate::query::{With, Without}; + use crate::system::{IntoSystem, Query, System}; + use crate::world::{Ref, World}; + + use super::*; + + #[test] + fn basic_inheritance() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component_id = world.register_component::(); + let component = CompA(6); + + let base = world.spawn(component).id(); + let inherited = world.spawn(InheritFrom(base)).id(); + world.flush(); + + let entity = world.get_entity(inherited).unwrap(); + assert_eq!(entity.get::(), Some(&component)); + assert_eq!( + entity.get_by_id(component_id).map(|c| { + // SAFETY: CompA is registered with component_id + unsafe { c.deref::() } + }), + Ok(&component) + ); + assert_eq!( + entity.get_ref::().map(Ref::into_inner), + Some(&component) + ); + + let component_ticks = world.get_entity(base).unwrap().get_change_ticks::(); + assert_eq!(entity.get_change_ticks::(), component_ticks); + assert_eq!(entity.get_change_ticks_by_id(component_id), component_ticks); + } + + #[test] + fn override_inherited() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component = CompA(7); + + let base = world.spawn(CompA(5)).id(); + let inherited = world.spawn((component, InheritFrom(base))).id(); + + let entity = world.get_entity(inherited).unwrap(); + assert_eq!(entity.get::(), Some(&component)); + } + + #[test] + fn recursive_inheritance() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component = CompA(6); + + let base = world.spawn(component).id(); + let inherited1 = world.spawn(InheritFrom(base)).id(); + let inherited2 = world.spawn(InheritFrom(inherited1)).id(); + + let entity = world.get_entity(inherited2).unwrap(); + assert_eq!(entity.get::(), Some(&component)); + } + + #[test] + fn move_inherited_entity_archetype() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompB(i32); + + let component1 = CompA(6); + let component2 = CompB(1); + + let base = world.spawn(component1).id(); + let level1 = world.spawn(InheritFrom(base)).id(); + let level2 = world.spawn(InheritFrom(level1)).id(); + + world.entity_mut(base).insert(component2); + + let entity = world.get_entity(level1).unwrap(); + assert_eq!(entity.get::(), Some(&component1)); + assert_eq!(entity.get::(), Some(&component2)); + let entity = world.get_entity(level2).unwrap(); + assert_eq!(entity.get::(), Some(&component1)); + assert_eq!(entity.get::(), Some(&component2)); + + world.entity_mut(base).remove::(); + let entity = world.get_entity(level1).unwrap(); + assert_eq!(entity.get::(), None); + assert_eq!(entity.get::(), Some(&component2)); + let entity = world.get_entity(level2).unwrap(); + assert_eq!(entity.get::(), None); + assert_eq!(entity.get::(), Some(&component2)); + + world.entity_mut(base).remove::(); + let entity = world.get_entity(level1).unwrap(); + assert_eq!(entity.get::(), None); + assert_eq!(entity.get::(), None); + let entity = world.get_entity(level2).unwrap(); + assert_eq!(entity.get::(), None); + assert_eq!(entity.get::(), None); + } + + #[test] + fn inherit_from_shared_table() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA; + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompB; + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompC; + + let base1 = world.spawn(CompA).id(); + let base2 = world.spawn(CompB).id(); + let _inherited1 = world.spawn((CompC, InheritFrom(base1))).id(); + let _inherited2 = world.spawn((CompC, InheritFrom(base2))).id(); + + let mut query = world.query::<&CompB>(); + assert_eq!(query.iter(&world).len(), 2); + } + + #[test] + fn inherited_with_normal_components() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA; + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompB; + + let base = world.spawn(CompA).id(); + let inherited = world.spawn((CompB, InheritFrom(base))).id(); + + let mut query = world.query::<(&CompB, &CompB)>(); + assert!(query.get(&world, inherited).is_ok()); + } + + #[test] + fn query_inherited_component() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component = CompA(6); + + let base = world.spawn(component).id(); + let inherited = world.spawn(InheritFrom(base)).id(); + world.flush(); + + let mut query = world.query::<&CompA>(); + assert_eq!(query.get(&world, inherited), Ok(&component)); + assert_eq!(query.iter(&world).map(|c| c.0).sum::(), 12); + + let mut query = world.query_filtered::>(); + assert_eq!(query.iter(&world).len(), 2); + } + + #[test] + fn skip_inherited_if_mutable() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component = CompA(6); + let component_id = world.register_component::(); + + let base = world.spawn(component).id(); + let inherited = world.spawn(InheritFrom(base)).id(); + + assert!(world.get_mut::(inherited).is_none()); + assert!(world.get_mut_by_id(inherited, component_id).is_none()); + + let mut query = world.query::<&mut CompA>(); + assert!(query.get(&world, inherited).is_err()); + assert_eq!(query.iter(&world).len(), 1); + } + + #[test] + fn inherited_components_circular() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA; + + let entity1 = world.spawn(CompA).id(); + let entity2 = world.spawn_empty().id(); + + world.entity_mut(entity1).insert(InheritFrom(entity2)); + world.entity_mut(entity2).insert(InheritFrom(entity1)); + } + + #[test] + fn inherited_components_with_despawned_base() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA; + + let base = world.spawn(CompA).id(); + let entity = world.spawn(InheritFrom(base)).id(); + + world.despawn(base); + + assert_eq!(world.entity(entity).get::(), None); + let mut query = world.query::<&CompA>(); + assert_eq!(query.iter(&world).next(), None); + } + + #[test] + fn inherited_components_ref() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(bool); + + let comp = CompA(true); + + let base = world.spawn(comp).id(); + let entity = world.spawn(InheritFrom(base)).id(); + + assert_eq!( + world.entity(entity).get_ref::().map(Ref::into_inner), + Some(&comp) + ); + let mut query = world.query::>(); + assert_eq!(query.iter(&world).next().map(Ref::into_inner), Some(&comp)); + } + + #[test] + fn inherited_components_system() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA; + + let base = world.spawn(CompA).id(); + let inherited = world.spawn(InheritFrom(base)).id(); + + let query_system = move |query: Query>| { + assert!(query.contains(base)); + assert!(query.contains(inherited)); + }; + + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + system.update_archetype_component_access(world.as_unsafe_world_cell()); + + system.run((), &mut world); + } + + #[test] + #[should_panic] + fn inherited_components_system_conflict() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let base = world.spawn(CompA(10)).id(); + let _inherited = world.spawn(InheritFrom(base)).id(); + + fn query_system( + _q1: Query<&mut CompA, With>, + _q2: Query<&CompA, Without>, + ) { + } + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + system.update_archetype_component_access(world.as_unsafe_world_cell()); + + system.run((), &mut world); + } + + #[test] + #[ignore = "Properly supporting required components needs first-class fragmenting value components or similar"] + fn inherited_with_required_components() { + let mut world = World::new(); + + #[derive(Component, Debug, Clone, Copy, PartialEq, Eq)] + #[require(CompB)] + struct CompA; + + #[derive(Component, Default, Debug, Clone, Copy, PartialEq, Eq)] + struct CompB(bool); + + let base = world.spawn(CompB(true)).id(); + let inherited = world.spawn((InheritFrom(base), CompA)).id(); + + assert_eq!(world.get::(inherited), Some(&CompB(true))); + } + + #[test] + #[ignore = "Mutable inherited components support is not yet implemented"] + fn inherited_mutable() { + let mut world = World::new(); + + #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] + struct CompA(i32); + + let component = CompA(6); + + let base = world.spawn(component).id(); + let inherited = world.spawn(InheritFrom(base)).id(); + + let mut comp = world.get_mut::(inherited).unwrap(); + comp.0 = 4; + world.flush(); + + let mut query = world.query::<&CompA>(); + assert_eq!(query.iter(&world).map(|c| c.0).sum::(), 10); + } +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 79ba938f4b749..466d49dc64a95 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -40,6 +40,7 @@ pub mod error; pub mod event; pub mod hierarchy; pub mod identifier; +pub mod inheritance; pub mod intern; pub mod label; pub mod name; From 5c77668e1e91ca8648cff6987b5b1228fe6ffab3 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:48:10 +0000 Subject: [PATCH 03/16] add `InheritedComponents` to world --- crates/bevy_ecs/src/world/component_constants.rs | 4 ++++ crates/bevy_ecs/src/world/mod.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/crates/bevy_ecs/src/world/component_constants.rs b/crates/bevy_ecs/src/world/component_constants.rs index ea2899c5f916a..23bda82a9c8d0 100644 --- a/crates/bevy_ecs/src/world/component_constants.rs +++ b/crates/bevy_ecs/src/world/component_constants.rs @@ -14,6 +14,10 @@ pub const ON_REPLACE: ComponentId = ComponentId::new(2); pub const ON_REMOVE: ComponentId = ComponentId::new(3); /// [`ComponentId`] for [`OnDespawn`] pub const ON_DESPAWN: ComponentId = ComponentId::new(4); +/// [`ComponentId`] for [`crate::inheritance::InheritFrom`] +pub const INHERIT_FROM: ComponentId = ComponentId::new(5); +/// [`ComponentId`] for [`crate::inheritance::Inherited`] +pub const INHERITED: ComponentId = ComponentId::new(6); /// Trigger emitted when a component is inserted onto an entity that does not already have that /// component. Runs before `OnInsert`. diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ad016f9e8aef6..d73f1f7a07074 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -14,6 +14,7 @@ pub mod unsafe_world_cell; #[cfg(feature = "bevy_reflect")] pub mod reflect; +use crate::inheritance::{Inherited, InheritedComponents}; pub use crate::{ change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD}, world::command_queue::CommandQueue, @@ -51,6 +52,7 @@ use crate::{ }, entity_disabling::DefaultQueryFilters, event::{Event, EventId, Events, SendBatchIds}, + inheritance::InheritFrom, observer::Observers, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, @@ -100,6 +102,7 @@ pub struct World { pub(crate) storages: Storages, pub(crate) bundles: Bundles, pub(crate) observers: Observers, + pub(crate) inherited_components: InheritedComponents, pub(crate) removed_components: RemovedComponentEvents, pub(crate) change_tick: AtomicU32, pub(crate) last_change_tick: Tick, @@ -127,6 +130,7 @@ impl Default for World { last_trigger_id: 0, command_queue: RawCommandQueue::new(), component_ids: ComponentIds::default(), + inherited_components: Default::default(), }; world.bootstrap(); world @@ -167,6 +171,12 @@ impl World { let on_despawn = OnDespawn::register_component_id(self); assert_eq!(ON_DESPAWN, on_despawn); + let inherit_from = self.register_component::(); + assert_eq!(INHERIT_FROM, inherit_from); + + let inherit_from = self.register_component::(); + assert_eq!(INHERITED, inherit_from); + // This sets up `Disabled` as a disabling component, via the FromWorld impl self.init_resource::(); } From e2eb140abf1ede28322bc16c9ca4039d622fcc0a Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:52:42 +0000 Subject: [PATCH 04/16] add inherited components support to `Archetype` and `Table` --- crates/bevy_ecs/src/archetype.rs | 49 ++++++++++++++++++++---- crates/bevy_ecs/src/storage/table/mod.rs | 18 ++++++++- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index f55acf6d35feb..9d7701661052c 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -25,6 +25,7 @@ use crate::{ entity::{Entity, EntityLocation}, observer::Observers, storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow}, + world::{INHERITED, INHERIT_FROM}, }; use alloc::{boxed::Box, vec::Vec}; use bevy_platform_support::collections::HashMap; @@ -360,6 +361,8 @@ bitflags::bitflags! { const ON_REPLACE_OBSERVER = (1 << 7); const ON_REMOVE_OBSERVER = (1 << 8); const ON_DESPAWN_OBSERVER = (1 << 9); + const HAS_INHERITED_COMPONENTS = (1 << 10); + const IS_INHERITED= (1 << 11); } } @@ -408,10 +411,16 @@ impl Archetype { // NOTE: the `table_components` are sorted AND they were inserted in the `Table` in the same // sorted order, so the index of the `Column` in the `Table` is the same as the index of the // component in the `table_components` vector - component_index - .entry(component_id) - .or_default() - .insert(id, ArchetypeRecord { column: Some(idx) }); + component_index.entry(component_id).or_default().insert( + id, + ArchetypeRecord { + column: Some(idx), + is_inherited: false, + }, + ); + if component_id == INHERIT_FROM { + flags.set(ArchetypeFlags::HAS_INHERITED_COMPONENTS, true); + } } for (component_id, archetype_component_id) in sparse_set_components { @@ -426,10 +435,16 @@ impl Archetype { archetype_component_id, }, ); - component_index - .entry(component_id) - .or_default() - .insert(id, ArchetypeRecord { column: None }); + component_index.entry(component_id).or_default().insert( + id, + ArchetypeRecord { + column: None, + is_inherited: false, + }, + ); + if component_id == INHERITED { + flags.set(ArchetypeFlags::IS_INHERITED, true); + } } Self { id, @@ -719,6 +734,23 @@ impl Archetype { pub fn has_despawn_observer(&self) -> bool { self.flags().contains(ArchetypeFlags::ON_DESPAWN_OBSERVER) } + + #[inline] + /// Returns `true` if this archetype has components inherited from another archetype. + /// + /// Use [`crate::inheritance::InheritedComponents`] to get get the list of inherited components. + pub fn has_inherited_components(&self) -> bool { + self.flags() + .contains(ArchetypeFlags::HAS_INHERITED_COMPONENTS) + } + + #[inline] + /// Returns `true` if this archetype is inherited by any other archetypes. + /// + /// Use [`crate::inheritance::InheritedComponents`] to get get the list of inherited components. + pub fn is_inherited(&self) -> bool { + self.flags().contains(ArchetypeFlags::IS_INHERITED) + } } /// The next [`ArchetypeId`] in an [`Archetypes`] collection. @@ -810,6 +842,7 @@ pub struct ArchetypeRecord { reason = "Currently unused, but planned to be used to implement a component index to improve performance of fragmenting relations." )] pub(crate) column: Option, + pub(crate) is_inherited: bool, } impl Archetypes { diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index d211bc10dae68..f21ce1554bbcc 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -4,6 +4,7 @@ use crate::{ entity::Entity, query::DebugCheckedUnwrap, storage::{blob_vec::BlobVec, ImmutableSparseSet, SparseSet}, + world::INHERIT_FROM, }; use alloc::{boxed::Box, vec, vec::Vec}; use bevy_platform_support::collections::HashMap; @@ -31,7 +32,7 @@ mod column; /// [`World`]: crate::world::World /// [`Archetype`]: crate::archetype::Archetype /// [`Archetype::table_id`]: crate::archetype::Archetype::table_id -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TableId(u32); impl TableId { @@ -148,6 +149,7 @@ impl TableRow { pub(crate) struct TableBuilder { columns: SparseSet, capacity: usize, + has_inherited_components: bool, } impl TableBuilder { @@ -156,6 +158,7 @@ impl TableBuilder { Self { columns: SparseSet::with_capacity(column_capacity), capacity, + has_inherited_components: false, } } @@ -166,6 +169,9 @@ impl TableBuilder { component_info.id(), ThinColumn::with_capacity(component_info, self.capacity), ); + if component_info.id() == INHERIT_FROM { + self.has_inherited_components = true; + } self } @@ -175,6 +181,7 @@ impl TableBuilder { Table { columns: self.columns.into_immutable(), entities: Vec::with_capacity(self.capacity), + has_inherited_components: self.has_inherited_components, } } } @@ -194,6 +201,7 @@ impl TableBuilder { pub struct Table { columns: ImmutableSparseSet, entities: Vec, + has_inherited_components: bool, } struct AbortOnPanic; @@ -630,6 +638,14 @@ impl Table { self.columns.values() } + /// Returns `true` if this table has components inherited from another table. + /// + /// Use [`crate::inheritance::InheritedComponents`] to get get the list of inherited components. + #[inline] + pub fn has_inherited_components(&self) -> bool { + self.has_inherited_components + } + /// Clears all of the stored components in the [`Table`]. pub(crate) fn clear(&mut self) { let len = self.entity_count(); From a5ca15f8f9cf07158a475c5d6503175a78096f64 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:54:08 +0000 Subject: [PATCH 05/16] Initialize and update inherited components. With this commit, `InheritedComponents` is actually populated whenever a new archetype and table is created, and is updated whenever an entity that is inherited moves archetypes. --- crates/bevy_ecs/src/bundle.rs | 51 +++++++++++++++++++++++++ crates/bevy_ecs/src/world/entity_ref.rs | 31 +++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index d87ef517f15d2..fdff4a9c57cc2 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -15,6 +15,7 @@ use crate::{ RequiredComponents, StorageType, Tick, }, entity::{Entities, Entity, EntityLocation}, + inheritance::InheritedComponents, observer::Observers, prelude::World, query::DebugCheckedUnwrap, @@ -733,6 +734,8 @@ impl BundleInfo { storages: &mut Storages, components: &Components, observers: &Observers, + entities: &Entities, + inherited_components: &mut InheritedComponents, archetype_id: ArchetypeId, ) -> ArchetypeId { if let Some(archetype_after_insert_id) = archetypes[archetype_id] @@ -836,6 +839,12 @@ impl BundleInfo { table_components, sparse_set_components, ); + inherited_components.init_inherited_components( + entities, + components, + archetypes, + new_archetype_id, + ); // Add an edge from the old archetype to the new archetype. archetypes[archetype_id] .edges_mut() @@ -872,6 +881,8 @@ impl BundleInfo { storages: &mut Storages, components: &Components, observers: &Observers, + entities: &Entities, + inherited_components: &mut InheritedComponents, archetype_id: ArchetypeId, intersection: bool, ) -> Option { @@ -947,6 +958,12 @@ impl BundleInfo { next_table_components, next_sparse_set_components, ); + inherited_components.init_inherited_components( + entities, + components, + archetypes, + new_archetype_id, + ); Some(new_archetype_id) }; let current_archetype = &mut archetypes[archetype_id]; @@ -1028,6 +1045,8 @@ impl<'w> BundleInserter<'w> { &mut world.storages, &world.components, &world.observers, + &world.entities, + &mut world.inherited_components, archetype_id, ); if new_archetype_id == archetype_id { @@ -1165,6 +1184,21 @@ impl<'w> BundleInserter<'w> { (archetype, location, after_effect) } ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => { + { + let new_archetype_id = new_archetype.as_ref().id(); + let old_archetype_id = self.archetype.as_ref().id(); + let world = self.world.world_mut(); + world + .inherited_components + .update_inherited_archetypes::( + &mut world.archetypes, + &world.components, + old_archetype_id, + new_archetype_id, + entity, + ); + } + let new_archetype = new_archetype.as_mut(); // SAFETY: Mutable references do not alias and will be dropped after this block @@ -1209,6 +1243,21 @@ impl<'w> BundleInserter<'w> { new_archetype, new_table, } => { + { + let new_archetype_id = new_archetype.as_ref().id(); + let old_archetype_id = self.archetype.as_ref().id(); + let world = self.world.world_mut(); + world + .inherited_components + .update_inherited_archetypes::( + &mut world.archetypes, + &world.components, + old_archetype_id, + new_archetype_id, + entity, + ); + } + let new_table = new_table.as_mut(); let new_archetype = new_archetype.as_mut(); @@ -1399,6 +1448,8 @@ impl<'w> BundleSpawner<'w> { &mut world.storages, &world.components, &world.observers, + &world.entities, + &mut world.inherited_components, ArchetypeId::EMPTY, ); let archetype = &mut world.archetypes[new_archetype_id]; diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 22383e86b36e9..c1f5d961a8788 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -14,6 +14,7 @@ use crate::{ EntityLocation, }, event::Event, + inheritance::InheritedComponents, observer::Observer, query::{Access, ReadOnlyQueryData}, relationship::RelationshipHookMode, @@ -2000,6 +2001,8 @@ impl<'w> EntityWorldMut<'w> { storages, ®istrator, &world.observers, + &world.entities, + &mut world.inherited_components, old_location.archetype_id, false, )? @@ -2036,6 +2039,7 @@ impl<'w> EntityWorldMut<'w> { let storages = &mut world.storages; let components = &mut world.components; let entities = &mut world.entities; + let inherited_components = &mut world.inherited_components; let removed_components = &mut world.removed_components; let entity = self.entity; @@ -2074,6 +2078,8 @@ impl<'w> EntityWorldMut<'w> { entities, archetypes, storages, + inherited_components, + components, new_archetype_id, ); } @@ -2099,6 +2105,8 @@ impl<'w> EntityWorldMut<'w> { entities: &mut Entities, archetypes: &mut Archetypes, storages: &mut Storages, + inherited_components: &mut InheritedComponents, + components: &Components, new_archetype_id: ArchetypeId, ) { let old_archetype = &mut archetypes[old_archetype_id]; @@ -2119,6 +2127,25 @@ impl<'w> EntityWorldMut<'w> { } let old_table_row = remove_result.table_row; let old_table_id = old_archetype.table_id(); + + if old_table_id == archetypes[new_archetype_id].table_id() { + inherited_components.update_inherited_archetypes::( + archetypes, + components, + old_archetype_id, + new_archetype_id, + entity, + ); + } else { + inherited_components.update_inherited_archetypes::( + archetypes, + components, + old_archetype_id, + new_archetype_id, + entity, + ); + } + let new_archetype = &mut archetypes[new_archetype_id]; let new_location = if old_table_id == new_archetype.table_id() { @@ -2185,6 +2212,8 @@ impl<'w> EntityWorldMut<'w> { &mut world.storages, &world.components, &world.observers, + &world.entities, + &mut world.inherited_components, location.archetype_id, // components from the bundle that are not present on the entity are ignored true, @@ -2247,6 +2276,8 @@ impl<'w> EntityWorldMut<'w> { &mut world.entities, &mut world.archetypes, &mut world.storages, + &mut world.inherited_components, + &world.components, new_archetype_id, ); From 51cf2ffcad6f866a4144df851a77cc32e1f5bd80 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:57:05 +0000 Subject: [PATCH 06/16] make `world.get_*` methods respect inherited components --- .../bevy_ecs/src/world/unsafe_world_cell.rs | 95 ++++++++++++++++--- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 681ac99c9b86a..91dcc6124a227 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -7,6 +7,7 @@ use crate::{ change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, entity::{ContainsEntity, Entities, Entity, EntityDoesNotExistError, EntityLocation}, + inheritance::InheritedComponents, observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, ReadOnlyQueryData}, @@ -296,6 +297,14 @@ impl<'w> UnsafeWorldCell<'w> { &unsafe { self.world_metadata() }.bundles } + /// Retrieves this world's [`InheritedComponents`] collection. + #[inline] + pub fn inherited_components(self) -> &'w InheritedComponents { + // SAFETY: + // - we only access world metadata + &unsafe { self.world_metadata() }.inherited_components + } + /// Gets the current change tick of this world. #[inline] pub fn change_tick(self) -> Tick { @@ -822,6 +831,7 @@ impl<'w> UnsafeEntityCell<'w> { T::STORAGE_TYPE, self.entity, self.location, + true, ) .map(|(value, cells, caller)| Ref { // SAFETY: returned component is of type T @@ -943,6 +953,7 @@ impl<'w> UnsafeEntityCell<'w> { T::STORAGE_TYPE, self.entity, self.location, + false, ) .map(|(value, cells, caller)| Mut { // SAFETY: returned component is of type T @@ -974,7 +985,21 @@ impl<'w> UnsafeEntityCell<'w> { .get(location.archetype_id) .debug_checked_unwrap() }; - if Q::matches_component_set(&state, &|id| archetype.contains(id)) { + + let archetype_inherited_components = if archetype.has_inherited_components() { + self.world + .inherited_components() + .archetype_inherited_components + .get(&archetype.id()) + } else { + None + }; + + if Q::matches_component_set(&state, &|id| { + archetype.contains(id) + || archetype_inherited_components + .is_some_and(|components| components.contains_key(&id)) + }) { // SAFETY: state was initialized above using the world passed into this function let mut fetch = unsafe { Q::init_fetch( @@ -1066,6 +1091,7 @@ impl<'w> UnsafeEntityCell<'w> { info.storage_type(), self.entity, self.location, + false, ) .map(|(value, cells, caller)| MutUntyped { // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime @@ -1114,6 +1140,7 @@ impl<'w> UnsafeEntityCell<'w> { info.storage_type(), self.entity, self.location, + false, ) .map(|(value, cells, caller)| MutUntyped { // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime @@ -1202,6 +1229,16 @@ unsafe fn get_component( } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get(entity), } + .or_else(|| { + let archetype = world.archetypes().get(location.archetype_id)?; + let (base, ..) = *world + .inherited_components() + .archetype_inherited_components + .get(&archetype.id())? + .get(&component_id)?; + let location = world.entities().get(base)?; + get_component(world, component_id, storage_type, base, location) + }) } /// Get an untyped pointer to a particular [`Component`] and its [`ComponentTicks`] @@ -1218,6 +1255,7 @@ unsafe fn get_component_and_ticks( storage_type: StorageType, entity: Entity, location: EntityLocation, + include_inherited: bool, ) -> Option<( Ptr<'_>, TickCells<'_>, @@ -1228,23 +1266,40 @@ unsafe fn get_component_and_ticks( let table = world.fetch_table(location)?; // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules - Some(( - table.get_component(component_id, location.table_row)?, - TickCells { - added: table - .get_added_tick(component_id, location.table_row) - .debug_checked_unwrap(), - changed: table - .get_changed_tick(component_id, location.table_row) - .debug_checked_unwrap(), - }, - table - .get_changed_by(component_id, location.table_row) - .map(|changed_by| changed_by.debug_checked_unwrap()), - )) + table + .get_component(component_id, location.table_row) + .map(|ptr| { + ( + ptr, + TickCells { + added: table + .get_added_tick(component_id, location.table_row) + .debug_checked_unwrap(), + changed: table + .get_changed_tick(component_id, location.table_row) + .debug_checked_unwrap(), + }, + table + .get_changed_by(component_id, location.table_row) + .map(|changed_by| changed_by.debug_checked_unwrap()), + ) + }) } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_with_ticks(entity), } + .or_else(|| { + if !include_inherited { + return None; + } + let archetype = world.archetypes().get(location.archetype_id)?; + let (base, ..) = *world + .inherited_components() + .archetype_inherited_components + .get(&archetype.id())? + .get(&component_id)?; + let location = world.entities().get(base)?; + get_component_and_ticks(world, component_id, storage_type, base, location, false) + }) } /// Get an untyped pointer to the [`ComponentTicks`] on a particular [`Entity`] @@ -1271,6 +1326,16 @@ unsafe fn get_ticks( } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_ticks(entity), } + .or_else(|| { + let archetype = world.archetypes().get(location.archetype_id)?; + let (base, ..) = *world + .inherited_components() + .archetype_inherited_components + .get(&archetype.id())? + .get(&component_id)?; + let location = world.entities().get(base)?; + get_ticks(world, component_id, storage_type, base, location) + }) } impl ContainsEntity for UnsafeEntityCell<'_> { From efc05d632cbd52f96baef0e8d775b9ad2d7e0603 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 8 Apr 2025 16:58:11 +0000 Subject: [PATCH 07/16] make `Query` respect inherited components --- crates/bevy_ecs/macros/src/lib.rs | 4 +- crates/bevy_ecs/src/query/fetch.rs | 224 ++++++++++++++++-- crates/bevy_ecs/src/query/state.rs | 85 +++++-- crates/bevy_ecs/src/system/commands/mod.rs | 2 + crates/bevy_ecs/src/system/function_system.rs | 20 +- crates/bevy_ecs/src/system/system_param.rs | 58 +++-- 6 files changed, 337 insertions(+), 56 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index a657765ac23f9..165dd6fc9a311 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -431,9 +431,9 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } } - unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) { + unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, inherited_components: &#path::inheritance::InheritedComponents, system_meta: &mut #path::system::SystemMeta) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta) } + unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, inherited_components, system_meta) } } fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) { diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index bd831e97fdf8e..51af030216aae 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -4,8 +4,9 @@ use crate::{ change_detection::{MaybeLocation, Ticks, TicksMut}, component::{Component, ComponentId, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, + inheritance::InheritedComponents, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, - storage::{ComponentSparseSet, Table, TableId, TableRow}, + storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, world::{ unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, @@ -1141,6 +1142,13 @@ pub struct ReadFetch<'w, T: Component> { // T::STORAGE_TYPE = StorageType::SparseSet Option<&'w ComponentSparseSet>, >, + // Stores all references needed to resolve inherited components. + inherited_components_data: ( + Option<&'w T>, + &'w Entities, + &'w Tables, + &'w InheritedComponents, + ), } impl Clone for ReadFetch<'_, T> { @@ -1181,6 +1189,14 @@ unsafe impl WorldQuery for &T { unsafe { world.storages().sparse_sets.get(component_id) } }, ), + inherited_components_data: ( + None, + world.entities(), + // SAFETY: This is used by inherited components to get inherited tables, + // which get registered as part of inheritor table registration. + unsafe { &world.storages().tables }, + world.inherited_components(), + ), } } @@ -1198,7 +1214,38 @@ unsafe impl WorldQuery for &T { archetype: &'w Archetype, table: &'w Table, ) { - if Self::IS_DENSE { + if archetype.has_inherited_components() && !archetype.contains(*component_id) { + let (inherited_component, entities, tables, inherited_components) = + &mut fetch.inherited_components_data; + let inherited_components = inherited_components + .archetype_inherited_components + .get(&archetype.id()) + .unwrap(); + let (entity, ..) = *inherited_components.get(component_id).unwrap(); + match T::STORAGE_TYPE { + StorageType::Table => { + let location = entities.get(entity).unwrap(); + let table = tables.get(location.table_id).unwrap(); + *inherited_component = Some( + table + .get_component(*component_id, location.table_row) + .unwrap() + .deref(), + ); + } + StorageType::SparseSet => { + *inherited_component = Some( + fetch + .components + .sparse_set + .unwrap() + .get(entity) + .unwrap() + .deref(), + ); + } + } + } else if Self::IS_DENSE { let table_id = archetype.table_id(); // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { @@ -1214,14 +1261,32 @@ unsafe impl WorldQuery for &T { table: &'w Table, table_id: TableId, ) { - let table_data = Some( - table - .get_data_slice_for(component_id) - .debug_checked_unwrap() - .into(), - ); - // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table - unsafe { fetch.components.set_table(table_data) }; + if table.has_inherited_components() && !table.has_column(component_id) { + let (inherited_component, entities, tables, inherited_components) = + &mut fetch.inherited_components_data; + let inherited_components = inherited_components + .table_inherited_components + .get(&table_id) + .unwrap(); + let entity = *inherited_components.get(&component_id).unwrap(); + let location = entities.get(entity).unwrap(); + let table = tables.get(location.table_id).unwrap(); + *inherited_component = Some( + table + .get_component(component_id, location.table_row) + .unwrap() + .deref(), + ); + } else { + let table_data = Some( + table + .get_data_slice_for(component_id) + .debug_checked_unwrap() + .into(), + ); + // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table + unsafe { fetch.components.set_table(table_data) }; + } } fn update_component_access( @@ -1268,6 +1333,9 @@ unsafe impl QueryData for &T { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { + if let Some(item) = fetch.inherited_components_data.0 { + return item; + } fetch.components.extract( |table| { // SAFETY: set_table was previously called @@ -1310,6 +1378,18 @@ pub struct RefFetch<'w, T: Component> { >, last_run: Tick, this_run: Tick, + // Stores all references needed to resolve inherited components. + inherited_components_data: ( + Option<( + &'w T, + &'w Tick, + &'w Tick, + MaybeLocation<&'w &'static Location<'static>>, + )>, + &'w Entities, + &'w Tables, + &'w InheritedComponents, + ), } impl Clone for RefFetch<'_, T> { @@ -1352,6 +1432,14 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { ), last_run, this_run, + inherited_components_data: ( + None, + world.entities(), + // SAFETY: This is used by inherited components to get inherited tables, + // which get registered as part of inheritor table registration. + unsafe { &world.storages().tables }, + world.inherited_components(), + ), } } @@ -1369,7 +1457,55 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { archetype: &'w Archetype, table: &'w Table, ) { - if Self::IS_DENSE { + if archetype.has_inherited_components() && !archetype.contains(*component_id) { + let (inherited_component, entities, tables, inherited_components) = + &mut fetch.inherited_components_data; + let inherited_components = inherited_components + .archetype_inherited_components + .get(&archetype.id()) + .unwrap(); + let (entity, ..) = *inherited_components.get(component_id).unwrap(); + match T::STORAGE_TYPE { + StorageType::Table => { + let location = entities.get(entity).unwrap(); + let table = tables.get(location.table_id).unwrap(); + + *inherited_component = Some(( + table + .get_component(*component_id, location.table_row) + .unwrap() + .deref(), + table + .get_added_tick(*component_id, location.table_row) + .unwrap() + .deref(), + table + .get_changed_tick(*component_id, location.table_row) + .unwrap() + .deref(), + table + .get_changed_by(*component_id, location.table_row) + .map(|c| c.unwrap().deref()), + )); + } + StorageType::SparseSet => { + let (component, ticks, caller) = { + fetch + .components + .sparse_set + .unwrap() + .get_with_ticks(entity) + .unwrap() + }; + *inherited_component = Some(( + component.deref(), + ticks.added.deref(), + ticks.changed.deref(), + caller.map(|c| c.deref()), + )); + } + } + } else if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { Self::set_table(fetch, component_id, table, archetype.table_id()); @@ -1382,19 +1518,49 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { fetch: &mut RefFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, - _table_id: TableId, + table_id: TableId, ) { - let column = table.get_column(component_id).debug_checked_unwrap(); - let table_data = Some(( - column.get_data_slice(table.entity_count()).into(), - column.get_added_ticks_slice(table.entity_count()).into(), - column.get_changed_ticks_slice(table.entity_count()).into(), - column - .get_changed_by_slice(table.entity_count()) - .map(Into::into), - )); - // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table - unsafe { fetch.components.set_table(table_data) }; + if table.has_inherited_components() && !table.has_column(component_id) { + let (inherited_component, entities, tables, inherited_components) = + &mut fetch.inherited_components_data; + let inherited_components = inherited_components + .table_inherited_components + .get(&table_id) + .unwrap(); + let entity = *inherited_components.get(&component_id).unwrap(); + let location = entities.get(entity).unwrap(); + let table = tables.get(location.table_id).unwrap(); + + *inherited_component = Some(( + table + .get_component(component_id, location.table_row) + .unwrap() + .deref(), + table + .get_added_tick(component_id, location.table_row) + .unwrap() + .deref(), + table + .get_changed_tick(component_id, location.table_row) + .unwrap() + .deref(), + table + .get_changed_by(component_id, location.table_row) + .map(|c| c.unwrap().deref()), + )); + } else { + let column = table.get_column(component_id).debug_checked_unwrap(); + let table_data = Some(( + column.get_data_slice(table.entity_count()).into(), + column.get_added_ticks_slice(table.entity_count()).into(), + column.get_changed_ticks_slice(table.entity_count()).into(), + column + .get_changed_by_slice(table.entity_count()) + .map(Into::into), + )); + // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table + unsafe { fetch.components.set_table(table_data) }; + } } fn update_component_access( @@ -1441,6 +1607,18 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { + if let Some((value, added, changed, changed_by)) = fetch.inherited_components_data.0 { + return Ref { + value, + ticks: Ticks { + added, + changed, + this_run: fetch.this_run, + last_run: fetch.last_run, + }, + changed_by, + }; + } fetch.components.extract( |table| { // SAFETY: set_table was previously called diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index e9a00f4646e1f..52d37aa7b4e56 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -3,6 +3,7 @@ use crate::{ component::{ComponentId, Tick}, entity::{Entity, EntityEquivalent, EntitySet, UniqueEntityArray}, entity_disabling::DefaultQueryFilters, + inheritance::InheritedComponents, prelude::FromWorld, query::{Access, FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, WorldQuery}, storage::{SparseSetIndex, TableId}, @@ -185,8 +186,12 @@ impl QueryState { // SAFETY: The state was just initialized from the `world` above, and the archetypes being added // come directly from the same world. unsafe { - if state.new_archetype_internal(archetype) { - state.update_archetype_component_access(archetype, access); + if state.new_archetype_internal(archetype, &world.inherited_components) { + state.update_archetype_component_access( + archetype, + &world.inherited_components, + access, + ); } } } @@ -538,7 +543,7 @@ impl QueryState { // SAFETY: The validate_world call ensures that the world is the same the QueryState // was initialized from. unsafe { - self.new_archetype_internal(archetype); + self.new_archetype_internal(archetype, world.inherited_components()); } } } else { @@ -573,7 +578,7 @@ impl QueryState { // SAFETY: The validate_world call ensures that the world is the same the QueryState // was initialized from. unsafe { - self.new_archetype_internal(archetype); + self.new_archetype_internal(archetype, world.inherited_components()); } } } @@ -612,13 +617,16 @@ impl QueryState { pub unsafe fn new_archetype( &mut self, archetype: &Archetype, + inherited_components: &InheritedComponents, access: &mut Access, ) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from. - let matches = unsafe { self.new_archetype_internal(archetype) }; + let matches = unsafe { self.new_archetype_internal(archetype, inherited_components) }; if matches { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from. - unsafe { self.update_archetype_component_access(archetype, access) }; + unsafe { + self.update_archetype_component_access(archetype, inherited_components, access); + }; } } @@ -630,11 +638,32 @@ impl QueryState { /// /// # Safety /// `archetype` must be from the `World` this state was initialized from. - unsafe fn new_archetype_internal(&mut self, archetype: &Archetype) -> bool { - if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id)) - && F::matches_component_set(&self.filter_state, &|id| archetype.contains(id)) - && self.matches_component_set(&|id| archetype.contains(id)) - { + unsafe fn new_archetype_internal( + &mut self, + archetype: &Archetype, + inherited_components: &InheritedComponents, + ) -> bool { + let is_matching = if archetype.has_inherited_components() { + let inherited_components = inherited_components + .archetype_inherited_components + .get(&archetype.id()) + .unwrap(); + D::matches_component_set(&self.fetch_state, &|id| { + archetype.contains(id) || inherited_components.contains_key(&id) + }) && F::matches_component_set(&self.filter_state, &|id| { + archetype.contains(id) || inherited_components.contains_key(&id) + }) && self.matches_component_set(&|id| { + archetype.contains(id) || inherited_components.contains_key(&id) + }) && !inherited_components + .keys() + .any(|id| self.component_access.access.has_component_write(*id)) + } else { + D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id)) + && F::matches_component_set(&self.filter_state, &|id| archetype.contains(id)) + && self.matches_component_set(&|id| archetype.contains(id)) + }; + + if is_matching { let archetype_index = archetype.id().index(); if !self.matched_archetypes.contains(archetype_index) { self.matched_archetypes.grow_and_insert(archetype_index); @@ -681,13 +710,28 @@ impl QueryState { pub unsafe fn update_archetype_component_access( &mut self, archetype: &Archetype, + inherited_components: &InheritedComponents, access: &mut Access, ) { // As a fast path, we can iterate directly over the components involved // if the `access` is finite. if let Ok(iter) = self.component_access.access.try_iter_component_access() { iter.for_each(|component_access| { - if let Some(id) = archetype.get_archetype_component_id(*component_access.index()) { + if let Some(id) = archetype + .get_archetype_component_id(*component_access.index()) + .or_else(|| { + if archetype.has_inherited_components() { + inherited_components + .archetype_inherited_components + .get(&archetype.id()) + .and_then(|map| { + map.get(component_access.index()).map(|(_, id)| *id) + }) + } else { + None + } + }) + { match component_access { ComponentAccessKind::Archetypal(_) => {} ComponentAccessKind::Shared(_) => { @@ -699,12 +743,25 @@ impl QueryState { } } }); - return; } + let empty_map = Default::default(); for (component_id, archetype_component_id) in - archetype.components_with_archetype_component_id() + archetype.components_with_archetype_component_id().chain( + if archetype.has_inherited_components() { + inherited_components + .archetype_inherited_components + .get(&archetype.id()) + .unwrap_or(&empty_map) + } else { + &empty_map + } + .iter() + .map(|(component_id, (_, archetype_component_id))| { + (*component_id, *archetype_component_id) + }), + ) { if self .component_access diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 8404f1db28172..2d67c4b04a6ea 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -136,6 +136,7 @@ const _: () = { unsafe fn new_archetype( state: &mut Self::State, archetype: &bevy_ecs::archetype::Archetype, + inherited_components: &bevy_ecs::inheritance::InheritedComponents, system_meta: &mut bevy_ecs::system::SystemMeta, ) { // SAFETY: Caller guarantees the archetype is from the world used in `init_state` @@ -143,6 +144,7 @@ const _: () = { <__StructFieldsAlias<'_, '_> as bevy_ecs::system::SystemParam>::new_archetype( &mut state.state, archetype, + inherited_components, system_meta, ); }; diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 4e05ef4dae360..567c6efcb8256 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -474,12 +474,20 @@ impl SystemState { assert_eq!(self.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with."); let archetypes = world.archetypes(); + let inherited_components = world.inherited_components(); let old_generation = core::mem::replace(&mut self.archetype_generation, archetypes.generation()); for archetype in &archetypes[old_generation..] { // SAFETY: The assertion above ensures that the param_state was initialized from `world`. - unsafe { Param::new_archetype(&mut self.param_state, archetype, &mut self.meta) }; + unsafe { + Param::new_archetype( + &mut self.param_state, + archetype, + inherited_components, + &mut self.meta, + ); + }; } } @@ -785,12 +793,20 @@ where assert_eq!(state.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with."); let archetypes = world.archetypes(); + let inherited_components = world.inherited_components(); let old_generation = core::mem::replace(&mut self.archetype_generation, archetypes.generation()); for archetype in &archetypes[old_generation..] { // SAFETY: The assertion above ensures that the param_state was initialized from `world`. - unsafe { F::Param::new_archetype(&mut state.param, archetype, &mut self.system_meta) }; + unsafe { + F::Param::new_archetype( + &mut state.param, + archetype, + inherited_components, + &mut self.system_meta, + ); + }; } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 99d4c72df66e2..3ec5801d14699 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -5,6 +5,7 @@ use crate::{ change_detection::{MaybeLocation, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Tick}, entity::Entities, + inheritance::InheritedComponents, query::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError, QueryState, ReadOnlyQueryData, @@ -236,6 +237,7 @@ pub unsafe trait SystemParam: Sized { unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { } @@ -351,9 +353,14 @@ unsafe impl SystemParam for Qu unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { - state.new_archetype(archetype, &mut system_meta.archetype_component_access); + state.new_archetype( + archetype, + inherited_components, + &mut system_meta.archetype_component_access, + ); } #[inline] @@ -422,10 +429,11 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam fo unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: Delegate to existing `SystemParam` implementations. - unsafe { Query::new_archetype(state, archetype, system_meta) }; + unsafe { Query::new_archetype(state, archetype, inherited_components, system_meta) }; } #[inline] @@ -492,10 +500,11 @@ unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: Delegate to existing `SystemParam` implementations. - unsafe { Single::new_archetype(state, archetype, system_meta) }; + unsafe { Single::new_archetype(state, archetype, inherited_components, system_meta) }; } #[inline] @@ -573,10 +582,11 @@ unsafe impl SystemParam unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: Delegate to existing `SystemParam` implementations. - unsafe { Query::new_archetype(state, archetype, system_meta) }; + unsafe { Query::new_archetype(state, archetype, inherited_components, system_meta) }; } #[inline] @@ -790,9 +800,9 @@ macro_rules! impl_param_set { ($($param,)*) } - unsafe fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) { + unsafe fn new_archetype(state: &mut Self::State, archetype: &Archetype, inherited_components: &InheritedComponents, system_meta: &mut SystemMeta) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { <($($param,)*) as SystemParam>::new_archetype(state, archetype, system_meta); } + unsafe { <($($param,)*) as SystemParam>::new_archetype(state, archetype, inherited_components, system_meta); } } fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) { @@ -1959,11 +1969,12 @@ unsafe impl SystemParam for Vec { unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { for state in state { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { T::new_archetype(state, archetype, system_meta) }; + unsafe { T::new_archetype(state, archetype, inherited_components, system_meta) }; } } @@ -2010,11 +2021,12 @@ unsafe impl SystemParam for ParamSet<'_, '_, Vec> { unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { for state in state { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { T::new_archetype(state, archetype, system_meta) } + unsafe { T::new_archetype(state, archetype, inherited_components, system_meta) } } } @@ -2093,13 +2105,13 @@ macro_rules! impl_system_param_tuple { } #[inline] - unsafe fn new_archetype(($($param,)*): &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) { + unsafe fn new_archetype(($($param,)*): &mut Self::State, archetype: &Archetype, inherited_components: &InheritedComponents, system_meta: &mut SystemMeta) { #[allow( unused_unsafe, reason = "Zero-length tuples will not run anything in the unsafe block." )] // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { $($param::new_archetype($param, archetype, system_meta);)* } + unsafe { $($param::new_archetype($param, archetype, inherited_components, system_meta);)* } } #[inline] @@ -2274,10 +2286,11 @@ unsafe impl SystemParam for StaticSystemParam<'_, '_, unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: The caller guarantees that the provided `archetype` matches the World used to initialize `state`. - unsafe { P::new_archetype(state, archetype, system_meta) }; + unsafe { P::new_archetype(state, archetype, inherited_components, system_meta) }; } fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) { @@ -2524,7 +2537,12 @@ trait DynParamState: Sync + Send { /// /// # Safety /// `archetype` must be from the [`World`] used to initialize `state` in [`SystemParam::init_state`]. - unsafe fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta); + unsafe fn new_archetype( + &mut self, + archetype: &Archetype, + inherited_components: &InheritedComponents, + system_meta: &mut SystemMeta, + ); /// Applies any deferred mutations stored in this [`SystemParam`]'s state. /// This is used to apply [`Commands`] during [`ApplyDeferred`](crate::prelude::ApplyDeferred). @@ -2554,9 +2572,14 @@ impl DynParamState for ParamState { self } - unsafe fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { + unsafe fn new_archetype( + &mut self, + archetype: &Archetype, + inherited_components: &InheritedComponents, + system_meta: &mut SystemMeta, + ) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { T::new_archetype(&mut self.0, archetype, system_meta) }; + unsafe { T::new_archetype(&mut self.0, archetype, inherited_components, system_meta) }; } fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) { @@ -2620,10 +2643,15 @@ unsafe impl SystemParam for DynSystemParam<'_, '_> { unsafe fn new_archetype( state: &mut Self::State, archetype: &Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. - unsafe { state.0.new_archetype(archetype, system_meta) }; + unsafe { + state + .0 + .new_archetype(archetype, inherited_components, system_meta); + }; } fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) { From ce6b4a8a034383b9fbb5d40c6f95521171f71211 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 10 Apr 2025 09:07:05 +0000 Subject: [PATCH 08/16] Fix iteration speed regression when not using inherited components. Inherited components are now stored internally on tables and archetypes, and `QueryData::fetch` can now resolve table components without a branch, which makes inherited components functionality effectively free. --- crates/bevy_ecs/src/archetype.rs | 18 + crates/bevy_ecs/src/bundle.rs | 64 +-- crates/bevy_ecs/src/inheritance.rs | 335 +++++++------- crates/bevy_ecs/src/query/fetch.rs | 413 +++++++++--------- crates/bevy_ecs/src/query/filter.rs | 8 +- crates/bevy_ecs/src/query/state.rs | 55 +-- crates/bevy_ecs/src/storage/table/mod.rs | 3 + crates/bevy_ecs/src/world/entity_ref.rs | 40 +- .../bevy_ecs/src/world/unsafe_world_cell.rs | 114 +++-- 9 files changed, 574 insertions(+), 476 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 9d7701661052c..fb52c490d785a 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -23,6 +23,7 @@ use crate::{ bundle::BundleId, component::{ComponentId, Components, RequiredComponentConstructor, StorageType}, entity::{Entity, EntityLocation}, + inheritance::InheritedArchetypeComponent, observer::Observers, storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow}, world::{INHERITED, INHERIT_FROM}, @@ -379,6 +380,7 @@ pub struct Archetype { entities: Vec, components: ImmutableSparseSet, pub(crate) flags: ArchetypeFlags, + pub(crate) inherited_components: HashMap, } impl Archetype { @@ -453,6 +455,7 @@ impl Archetype { components: archetype_components.into_immutable(), edges: Default::default(), flags, + inherited_components: Default::default(), } } @@ -532,6 +535,16 @@ impl Archetype { .map(|(component_id, info)| (*component_id, info.archetype_component_id)) } + pub(crate) fn components_with_inherited_and_archetype_component_id( + &self, + ) -> impl Iterator + '_ { + self.components_with_archetype_component_id().chain( + self.inherited_components + .iter() + .map(|(component_id, info)| (*component_id, info.archetype_component_id())), + ) + } + /// Fetches an immutable reference to the archetype's [`Edges`], a cache of /// archetypal relationships. #[inline] @@ -637,6 +650,11 @@ impl Archetype { self.components.contains(component_id) } + #[inline] + pub fn contains_with_inherited(&self, component_id: ComponentId) -> bool { + self.contains(component_id) || self.inherited_components.contains_key(&component_id) + } + /// Gets the type of storage where a component in the archetype can be found. /// Returns `None` if the component is not part of the archetype. /// This runs in `O(1)` time. diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index fdff4a9c57cc2..d14625e9aa876 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -841,8 +841,8 @@ impl BundleInfo { ); inherited_components.init_inherited_components( entities, - components, archetypes, + &mut storages.tables, new_archetype_id, ); // Add an edge from the old archetype to the new archetype. @@ -960,8 +960,8 @@ impl BundleInfo { ); inherited_components.init_inherited_components( entities, - components, archetypes, + &mut storages.tables, new_archetype_id, ); Some(new_archetype_id) @@ -1184,21 +1184,6 @@ impl<'w> BundleInserter<'w> { (archetype, location, after_effect) } ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => { - { - let new_archetype_id = new_archetype.as_ref().id(); - let old_archetype_id = self.archetype.as_ref().id(); - let world = self.world.world_mut(); - world - .inherited_components - .update_inherited_archetypes::( - &mut world.archetypes, - &world.components, - old_archetype_id, - new_archetype_id, - entity, - ); - } - let new_archetype = new_archetype.as_mut(); // SAFETY: Mutable references do not alias and will be dropped after this block @@ -1243,21 +1228,6 @@ impl<'w> BundleInserter<'w> { new_archetype, new_table, } => { - { - let new_archetype_id = new_archetype.as_ref().id(); - let old_archetype_id = self.archetype.as_ref().id(); - let world = self.world.world_mut(); - world - .inherited_components - .update_inherited_archetypes::( - &mut world.archetypes, - &world.components, - old_archetype_id, - new_archetype_id, - entity, - ); - } - let new_table = new_table.as_mut(); let new_archetype = new_archetype.as_mut(); @@ -1339,6 +1309,36 @@ impl<'w> BundleInserter<'w> { }; let new_archetype = &*new_archetype; + + if new_archetype.is_inherited() { + let new_archetype_id = new_location.archetype_id; + let old_archetype_id = location.archetype_id; + let world = self.world.world_mut(); + if new_location.table_id != location.table_id { + world + .inherited_components + .update_inherited_archetypes::( + &mut world.archetypes, + &mut world.storages.tables, + new_archetype_id, + old_archetype_id, + entity, + new_location, + ); + } else { + world + .inherited_components + .update_inherited_archetypes::( + &mut world.archetypes, + &mut world.storages.tables, + new_archetype_id, + old_archetype_id, + entity, + new_location, + ); + } + } + // SAFETY: We have no outstanding mutable references to world as they were dropped let mut deferred_world = unsafe { self.world.into_deferred() }; diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index bda8ae9aa4f86..a4f8c380b36b7 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -15,11 +15,11 @@ use bevy_ptr::OwningPtr; use crate::{ archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, component::{ - Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, Components, - HookContext, StorageType, + Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, HookContext, + StorageType, }, - entity::{Entities, Entity, EntityHashMap}, - storage::TableId, + entity::{Entities, Entity, EntityHashMap, EntityLocation}, + storage::{TableId, TableRow, Tables}, world::{DeferredWorld, World}, }; @@ -139,6 +139,39 @@ impl Inherited { } } +#[derive(Debug, Clone, Copy)] +pub(crate) enum InheritedArchetypeComponent { + Sparse { + archetype_component_id: ArchetypeComponentId, + entity: Entity, + }, + Table { + archetype_component_id: ArchetypeComponentId, + table_id: TableId, + table_row: TableRow, + }, +} + +impl InheritedArchetypeComponent { + pub fn archetype_component_id(&self) -> ArchetypeComponentId { + match self { + InheritedArchetypeComponent::Table { + archetype_component_id, + .. + } + | InheritedArchetypeComponent::Sparse { + archetype_component_id, + .. + } => *archetype_component_id, + } + } +} + +pub(crate) struct InheritedTableComponent { + pub(crate) table_id: TableId, + pub(crate) table_row: TableRow, +} + #[derive(Default)] /// Contains information about inherited components and entities. /// @@ -151,11 +184,11 @@ pub struct InheritedComponents { /// Mapping of component ids to entities representing the inherited archetypes and tables. /// Must be kept synchronized with `entities_to_ids` pub(crate) ids_to_entities: HashMap, - /// List of inherited components along with entities containing the inherited component for the archetypes - pub(crate) archetype_inherited_components: - HashMap>, - /// List of inherited components along with entities containing the inherited component for the tables - pub(crate) table_inherited_components: HashMap>, + // /// List of inherited components along with entities containing the inherited component for the archetypes + // pub(crate) archetype_inherited_components: + // HashMap>, + // /// List of inherited components along with entities containing the inherited component for the tables + // pub(crate) table_inherited_components: HashMap>, } impl InheritedComponents { @@ -163,48 +196,56 @@ impl InheritedComponents { pub(crate) fn init_inherited_components( &mut self, entities: &Entities, - components: &Components, archetypes: &mut Archetypes, + tables: &mut Tables, archetype_id: ArchetypeId, ) { let archetype = &archetypes.archetypes[archetype_id.index()]; if !archetype.has_inherited_components() { return; } - // Empty inherited components hashmap to enable .chain - let empty_inherited_components = HashMap::default(); - let archetype_inherited_components = HashMap::from_iter( + let archetype_inherited_components: HashMap = archetype .table_components() .filter_map(|id| { - self.ids_to_entities - .get(&id) - .and_then(|entity| { - entities.get(*entity).map(|location| (location, *entity)) - }) - .and_then(|(location, entity)| { + self.ids_to_entities.get(&id).and_then(|entity| { + entities.get(*entity).and_then(|location| { archetypes .get(location.archetype_id) - .map(|archetype| (archetype, entity)) + .map(|archetype| (archetype, *entity, location)) }) + }) }) - .flat_map(|(inherited_archetype, entity)| { + .flat_map(|(inherited_archetype, entity, location)| { inherited_archetype .components_with_archetype_component_id() - .zip(core::iter::repeat(entity)) - .map(|((component_id, archetype_component_id), entity)| { - (component_id, (entity, archetype_component_id)) + .map(move |(component_id, archetype_component_id)| { + ( + component_id, + match inherited_archetype.get_storage_type(component_id).unwrap() { + StorageType::Table => InheritedArchetypeComponent::Table { + archetype_component_id, + table_id: location.table_id, + table_row: location.table_row, + }, + StorageType::SparseSet => InheritedArchetypeComponent::Sparse { + archetype_component_id, + entity, + }, + }, + ) }) .chain( - self.archetype_inherited_components - .get(&inherited_archetype.id()) - .unwrap_or(&empty_inherited_components) + archetypes + .get(inherited_archetype.id()) + .unwrap() + .inherited_components .iter() - .map(|(&id, &entity)| (id, entity)), + .map(|(a, b)| (*a, *b)), ) }) - .filter(|(id, ..)| !archetype.contains(*id)), - ); + .filter(|(component, ..)| !archetype.contains(*component)) + .collect(); // Update component index to include this archetype in all the inherited components. for (inherited_component, ..) in archetype_inherited_components.iter() { @@ -225,166 +266,160 @@ impl InheritedComponents { // Since the only difference between archetypes with the same table is in sparse set components, // we can skip reinitializing table's inherited components. // `update_inherited_components` will take care of updating existing table's components. - self.table_inherited_components - .entry(archetype.table_id()) - .or_insert_with(|| { - HashMap::from_iter( - archetype_inherited_components - .iter() - .map(|(id, (e, ..))| (*id, *e)) - .filter(|(id, ..)| { - components.get_info(*id).unwrap().storage_type() == StorageType::Table - }), - ) - }); + let table_id = archetype.table_id(); + if tables[table_id].inherited_components.is_empty() { + let inherited_components = archetype_inherited_components + .iter() + .filter_map(|(&component_id, component)| match component { + InheritedArchetypeComponent::Sparse { .. } => None, + &InheritedArchetypeComponent::Table { + table_id, + table_row, + .. + } => Some(( + component_id, + InheritedTableComponent { + table_id, + table_row, + }, + )), + }) + .collect(); + tables[table_id].inherited_components = inherited_components; + } - self.archetype_inherited_components - .insert(archetype_id, archetype_inherited_components); + let archetype = &mut archetypes[archetype_id]; + archetype.inherited_components = archetype_inherited_components; } - /// This method must be called after an entity changes archetype to update all archetypes inheriting components from this entity. + /// This method must be called after an entity moves to update all inherited archetypes/tables. pub(crate) fn update_inherited_archetypes( &mut self, archetypes: &mut Archetypes, - components: &Components, - old_base_archetype_id: ArchetypeId, - new_base_archetype_id: ArchetypeId, + tables: &mut Tables, + new_archetype_id: ArchetypeId, + old_archetype_id: ArchetypeId, entity: Entity, + new_location: EntityLocation, ) { let Some(component_id) = self.entities_to_ids.get(&entity) else { return; }; - let old_base_archetype = &archetypes.archetypes[old_base_archetype_id.index()]; - let new_base_archetype = &archetypes.archetypes[new_base_archetype_id.index()]; - let added_components = new_base_archetype + let new_archetype = &archetypes[new_archetype_id]; + let new_components: HashMap<_, _> = new_archetype .components_with_archetype_component_id() - .filter(|(component_id, ..)| !old_base_archetype.contains(*component_id)) - .collect::>(); - let mut removed_components = old_base_archetype + .map(|(component_id, archetype_component_id)| { + ( + component_id, + ( + archetype_component_id, + new_archetype.get_storage_type(component_id).unwrap(), + ), + ) + }) + .collect(); + let removed_components: HashSet<_> = archetypes[old_archetype_id] .components() - .filter(|component_id| !new_base_archetype.contains(*component_id)) - .collect::>(); - removed_components.sort_unstable(); + .filter(|&component_id| !new_archetype.contains(component_id)) + .collect(); - let mut inherited_entities_queue = VecDeque::from([(entity, *component_id)]); - let mut archetypes_to_update = Vec::new(); + let mut base_entities = VecDeque::from([(entity, *component_id)]); + let mut inherited_archetypes = Vec::new(); let mut processed_archetypes = HashSet::::default(); - while let Some((entity, component_id)) = inherited_entities_queue.pop_front() { - archetypes_to_update.extend( - archetypes - .by_component - .get(&component_id) - .unwrap() - .keys() - .copied() - .filter(|archetype_id| !processed_archetypes.contains(archetype_id)), - ); - for archetype in archetypes_to_update.drain(..) { + while let Some((entity, inherited_entity_component_id)) = base_entities.pop_front() { + if let Some(map) = archetypes.by_component.get(&inherited_entity_component_id) { + inherited_archetypes.extend( + map.keys() + .copied() + .filter(|archetype_id| !processed_archetypes.contains(archetype_id)), + ); + }; + for archetype_id in inherited_archetypes.drain(..) { // Update archetype's inherited components - let archetype = &archetypes.archetypes[archetype.index()]; - let archetype_inherited_components = self - .archetype_inherited_components - .entry(archetype.id()) - .or_default(); + let mut archetype_inherited_components = + core::mem::take(&mut archetypes[archetype_id].inherited_components); archetype_inherited_components.retain(|component_id, _| { - removed_components.binary_search(component_id).is_err() + !new_components.contains_key(component_id) + && !removed_components.contains(component_id) }); - archetype_inherited_components.extend( - added_components - .iter() - .copied() - .filter(|(component_id, ..)| !archetype.contains(*component_id)) - .zip(core::iter::repeat(entity)) - .map(|((component_id, archetype_component_id), entity)| { - (component_id, (entity, archetype_component_id)) - }), - ); + archetype_inherited_components.extend(new_components.iter().map( + |(&component_id, &(archetype_component_id, storage_type))| { + ( + component_id, + match storage_type { + StorageType::Table => InheritedArchetypeComponent::Table { + archetype_component_id, + table_id: new_location.table_id, + table_row: new_location.table_row, + }, + StorageType::SparseSet => InheritedArchetypeComponent::Sparse { + archetype_component_id, + entity, + }, + }, + ) + }, + )); + archetypes[archetype_id].inherited_components = archetype_inherited_components; // Update component index - for removed_component in &removed_components { - archetypes - .by_component - .entry(*removed_component) - .and_modify(|map| { - if let Some(record) = map.get(&archetype.id()) { - if !record.is_inherited { - return; - } - } - map.remove(&archetype.id()); - }); - } - for (added_component, ..) in &added_components { - archetypes - .by_component - .entry(*added_component) - .and_modify(|map| { - if !map.contains_key(&archetype.id()) { - map.insert( - archetype.id(), - ArchetypeRecord { - column: None, - is_inherited: true, - }, - ); - } - }); + for &component in new_components.keys() { + archetypes.by_component.entry(component).and_modify(|map| { + map.insert( + new_archetype_id, + ArchetypeRecord { + column: None, + is_inherited: true, + }, + ); + }); } - // Update tables's inherited components + // Update archetype table's inherited components + // This needs to be done only if entity moved tables if UPDATE_TABLES { - let table_inherited_components = self - .table_inherited_components - .entry(archetype.table_id()) - .or_default(); + let table_id = archetypes[archetype_id].table_id(); + let mut table_inherited_components = + core::mem::take(&mut tables[table_id].inherited_components); + let table = &tables[table_id]; table_inherited_components.retain(|component_id, _| { - removed_components.binary_search(component_id).is_err() + !new_components.contains_key(component_id) + && !removed_components.contains(component_id) }); - table_inherited_components.extend( - added_components - .iter() - .map(|(component_id, ..)| *component_id) - .filter(|component_id| { - !archetype.contains(*component_id) - && components.get_info(*component_id).unwrap().storage_type() - == StorageType::Table - }) - .zip(core::iter::repeat(entity)), - ); + table_inherited_components.extend(new_components.iter().filter_map( + |(&component_id, &(_, storage_type))| { + if !table.has_column(component_id) && storage_type == StorageType::Table + { + Some(( + component_id, + InheritedTableComponent { + table_id: new_location.table_id, + table_row: new_location.table_row, + }, + )) + } else { + None + } + }, + )); + tables[table_id].inherited_components = table_inherited_components; } + let archetype = &archetypes[archetype_id]; if archetype.is_inherited() { for (entity, component_id) in archetype.entities().iter().filter_map(|entity| { self.entities_to_ids .get(&entity.id()) .map(|component_id| (entity.id(), *component_id)) }) { - inherited_entities_queue.push_back((entity, component_id)); + base_entities.push_back((entity, component_id)); } processed_archetypes.insert(archetype.id()); } } } } - - /// Returns an iterator yielding all archetypes acting as a base for the passed archetype. - pub fn get_base_archetypes( - &self, - entities: &Entities, - archetype_id: ArchetypeId, - ) -> impl Iterator { - HashSet::::from_iter( - self.archetype_inherited_components - .get(&archetype_id) - .unwrap() - .values() - .filter_map(|(entity, ..)| { - entities.get(*entity).map(|location| location.archetype_id) - }), - ) - .into_iter() - } } #[cfg(test)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 51af030216aae..a3e279e4bd8b2 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -4,7 +4,7 @@ use crate::{ change_detection::{MaybeLocation, Ticks, TicksMut}, component::{Component, ComponentId, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, - inheritance::InheritedComponents, + inheritance::InheritedArchetypeComponent, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, world::{ @@ -1142,13 +1142,7 @@ pub struct ReadFetch<'w, T: Component> { // T::STORAGE_TYPE = StorageType::SparseSet Option<&'w ComponentSparseSet>, >, - // Stores all references needed to resolve inherited components. - inherited_components_data: ( - Option<&'w T>, - &'w Entities, - &'w Tables, - &'w InheritedComponents, - ), + inherited_tables: &'w Tables, } impl Clone for ReadFetch<'_, T> { @@ -1189,14 +1183,9 @@ unsafe impl WorldQuery for &T { unsafe { world.storages().sparse_sets.get(component_id) } }, ), - inherited_components_data: ( - None, - world.entities(), - // SAFETY: This is used by inherited components to get inherited tables, - // which get registered as part of inheritor table registration. - unsafe { &world.storages().tables }, - world.inherited_components(), - ), + // SAFETY: This variable will be used only to access tables that contain inherited components, which + // we are allowed to access since `update_archetype_component_access` is aware of inherited components. + inherited_tables: unsafe { &world.storages().tables }, } } @@ -1214,43 +1203,29 @@ unsafe impl WorldQuery for &T { archetype: &'w Archetype, table: &'w Table, ) { - if archetype.has_inherited_components() && !archetype.contains(*component_id) { - let (inherited_component, entities, tables, inherited_components) = - &mut fetch.inherited_components_data; - let inherited_components = inherited_components - .archetype_inherited_components - .get(&archetype.id()) - .unwrap(); - let (entity, ..) = *inherited_components.get(component_id).unwrap(); - match T::STORAGE_TYPE { - StorageType::Table => { - let location = entities.get(entity).unwrap(); - let table = tables.get(location.table_id).unwrap(); - *inherited_component = Some( - table - .get_component(*component_id, location.table_row) - .unwrap() - .deref(), - ); - } - StorageType::SparseSet => { - *inherited_component = Some( - fetch - .components - .sparse_set - .unwrap() - .get(entity) - .unwrap() - .deref(), - ); - } - } - } else if Self::IS_DENSE { + if Self::IS_DENSE { let table_id = archetype.table_id(); // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { Self::set_table(fetch, component_id, table, table_id); } + } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + match archetype + .inherited_components + .get(component_id) + .debug_checked_unwrap() + { + &InheritedArchetypeComponent::Sparse { entity, .. } => { + // SAFETY: + // - T is a SparseSet component, otherwise Self::IS_DENSE would've been true + // - Current archetype does not contain the component, therefore it must be inherited and + // must be set using the provided entity. + unsafe { + fetch.components.set_sparse_set_entity(entity); + } + } + _ => unreachable!(), + } } } @@ -1262,21 +1237,34 @@ unsafe impl WorldQuery for &T { table_id: TableId, ) { if table.has_inherited_components() && !table.has_column(component_id) { - let (inherited_component, entities, tables, inherited_components) = - &mut fetch.inherited_components_data; - let inherited_components = inherited_components - .table_inherited_components - .get(&table_id) - .unwrap(); - let entity = *inherited_components.get(&component_id).unwrap(); - let location = entities.get(entity).unwrap(); - let table = tables.get(location.table_id).unwrap(); - *inherited_component = Some( - table - .get_component(component_id, location.table_row) - .unwrap() - .deref(), + let info = table + .inherited_components + .get(&component_id) + .debug_checked_unwrap(); + let table = fetch + .inherited_tables + .get(info.table_id) + .debug_checked_unwrap(); + let component_ptr = table + .get_component(component_id, info.table_row) + .debug_checked_unwrap(); + let table_data = Some( + core::slice::from_raw_parts::<'w, UnsafeCell>( + component_ptr.as_ptr() as *const _, + 1, + ) + .into(), ); + // SAFETY: + // - set_table is only called when T::STORAGE_TYPE = StorageType::Table + // - Current table doesn't contain the component, therefore it must be inherited and + // so it must be set as the same component for all entities within this table. + // This is achieved by setting the table_data as the pointer to this component + // and forcing fetch to access only the first element of the column for all entities. + unsafe { + fetch.components.set_table(table_data); + fetch.components.set_table_idx_mask(0); + }; } else { let table_data = Some( table @@ -1333,18 +1321,21 @@ unsafe impl QueryData for &T { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - if let Some(item) = fetch.inherited_components_data.0 { - return item; - } fetch.components.extract( - |table| { + |table, mask| { // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: Caller ensures `table_row` is in range. - let item = unsafe { table.get(table_row.as_usize()) }; + let item = unsafe { table.get(table_row.as_usize() & mask) }; item.deref() }, - |sparse_set| { + |sparse_set, entity_override| { + let entity = if entity_override == Entity::PLACEHOLDER { + entity + } else { + entity_override + }; + // SAFETY: Caller ensures `entity` is in range. let item = unsafe { sparse_set @@ -1378,18 +1369,7 @@ pub struct RefFetch<'w, T: Component> { >, last_run: Tick, this_run: Tick, - // Stores all references needed to resolve inherited components. - inherited_components_data: ( - Option<( - &'w T, - &'w Tick, - &'w Tick, - MaybeLocation<&'w &'static Location<'static>>, - )>, - &'w Entities, - &'w Tables, - &'w InheritedComponents, - ), + inherited_tables: &'w Tables, } impl Clone for RefFetch<'_, T> { @@ -1432,14 +1412,9 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { ), last_run, this_run, - inherited_components_data: ( - None, - world.entities(), - // SAFETY: This is used by inherited components to get inherited tables, - // which get registered as part of inheritor table registration. - unsafe { &world.storages().tables }, - world.inherited_components(), - ), + // SAFETY: This variable will be used only to access tables that contain inherited components, which + // we are allowed to access since `update_archetype_component_access` is aware of inherited components. + inherited_tables: unsafe { &world.storages().tables }, } } @@ -1457,59 +1432,28 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { archetype: &'w Archetype, table: &'w Table, ) { - if archetype.has_inherited_components() && !archetype.contains(*component_id) { - let (inherited_component, entities, tables, inherited_components) = - &mut fetch.inherited_components_data; - let inherited_components = inherited_components - .archetype_inherited_components - .get(&archetype.id()) - .unwrap(); - let (entity, ..) = *inherited_components.get(component_id).unwrap(); - match T::STORAGE_TYPE { - StorageType::Table => { - let location = entities.get(entity).unwrap(); - let table = tables.get(location.table_id).unwrap(); - - *inherited_component = Some(( - table - .get_component(*component_id, location.table_row) - .unwrap() - .deref(), - table - .get_added_tick(*component_id, location.table_row) - .unwrap() - .deref(), - table - .get_changed_tick(*component_id, location.table_row) - .unwrap() - .deref(), - table - .get_changed_by(*component_id, location.table_row) - .map(|c| c.unwrap().deref()), - )); - } - StorageType::SparseSet => { - let (component, ticks, caller) = { - fetch - .components - .sparse_set - .unwrap() - .get_with_ticks(entity) - .unwrap() - }; - *inherited_component = Some(( - component.deref(), - ticks.added.deref(), - ticks.changed.deref(), - caller.map(|c| c.deref()), - )); - } - } - } else if Self::IS_DENSE { + if Self::IS_DENSE { // SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones. unsafe { Self::set_table(fetch, component_id, table, archetype.table_id()); } + } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + match archetype + .inherited_components + .get(component_id) + .debug_checked_unwrap() + { + &InheritedArchetypeComponent::Sparse { entity, .. } => { + // SAFETY: + // - T is a SparseSet component, otherwise Self::IS_DENSE would've been true + // - Current archetype does not contain the component, therefore it must be inherited and + // must be set using the provided entity. + unsafe { + fetch.components.set_sparse_set_entity(entity); + } + } + _ => unreachable!(), + } } } @@ -1521,33 +1465,62 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { table_id: TableId, ) { if table.has_inherited_components() && !table.has_column(component_id) { - let (inherited_component, entities, tables, inherited_components) = - &mut fetch.inherited_components_data; - let inherited_components = inherited_components - .table_inherited_components - .get(&table_id) - .unwrap(); - let entity = *inherited_components.get(&component_id).unwrap(); - let location = entities.get(entity).unwrap(); - let table = tables.get(location.table_id).unwrap(); - - *inherited_component = Some(( - table - .get_component(component_id, location.table_row) - .unwrap() - .deref(), - table - .get_added_tick(component_id, location.table_row) - .unwrap() - .deref(), - table - .get_changed_tick(component_id, location.table_row) - .unwrap() - .deref(), + let info = table + .inherited_components + .get(&component_id) + .debug_checked_unwrap(); + + let table = fetch + .inherited_tables + .get(info.table_id) + .debug_checked_unwrap(); + let table_data = Some(( + core::slice::from_raw_parts::<'w, UnsafeCell>( + table + .get_component(component_id, info.table_row) + .debug_checked_unwrap() + .as_ptr() as *const _, + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_added_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_changed_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), table - .get_changed_by(component_id, location.table_row) - .map(|c| c.unwrap().deref()), + .get_changed_by(component_id, info.table_row) + .map(|loc| { + core::slice::from_raw_parts( + core::ptr::from_ref(loc.debug_checked_unwrap()), + 1, + ) + .into() + }), )); + // SAFETY: + // - set_table is only called when T::STORAGE_TYPE = StorageType::Table + // - Current table doesn't contain the component, therefore it must be inherited and + // so it must be set as the same component for all entities within this table. + // This is achieved by setting the table_data as the pointer to this component + // and forcing fetch to access only the first element of the column for all entities. + unsafe { + fetch.components.set_table(table_data); + fetch.components.set_table_idx_mask(0); + }; } else { let column = table.get_column(component_id).debug_checked_unwrap(); let table_data = Some(( @@ -1607,32 +1580,21 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - if let Some((value, added, changed, changed_by)) = fetch.inherited_components_data.0 { - return Ref { - value, - ticks: Ticks { - added, - changed, - this_run: fetch.this_run, - last_run: fetch.last_run, - }, - changed_by, - }; - } fetch.components.extract( - |table| { + |table, mask| { // SAFETY: set_table was previously called let (table_components, added_ticks, changed_ticks, callers) = unsafe { table.debug_checked_unwrap() }; + let index = table_row.as_usize() & mask; // SAFETY: The caller ensures `table_row` is in range. - let component = unsafe { table_components.get(table_row.as_usize()) }; + let component = unsafe { table_components.get(index) }; // SAFETY: The caller ensures `table_row` is in range. - let added = unsafe { added_ticks.get(table_row.as_usize()) }; + let added = unsafe { added_ticks.get(index) }; // SAFETY: The caller ensures `table_row` is in range. - let changed = unsafe { changed_ticks.get(table_row.as_usize()) }; + let changed = unsafe { changed_ticks.get(index) }; // SAFETY: The caller ensures `table_row` is in range. - let caller = callers.map(|callers| unsafe { callers.get(table_row.as_usize()) }); + let caller = callers.map(|callers| unsafe { callers.get(index) }); Ref { value: component.deref(), @@ -1645,7 +1607,13 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { changed_by: caller.map(|caller| caller.deref()), } }, - |sparse_set| { + |sparse_set, entity_override| { + let entity = if entity_override == Entity::PLACEHOLDER { + entity + } else { + entity_override + }; + // SAFETY: The caller ensures `entity` is in range and has the component. let (component, ticks, caller) = unsafe { sparse_set @@ -1816,7 +1784,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T table_row: TableRow, ) -> Self::Item<'w> { fetch.components.extract( - |table| { + |table, _| { // SAFETY: set_table was previously called let (table_components, added_ticks, changed_ticks, callers) = unsafe { table.debug_checked_unwrap() }; @@ -1841,7 +1809,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T changed_by: caller.map(|caller| caller.deref_mut()), } }, - |sparse_set| { + |sparse_set, _| { // SAFETY: The caller ensures `entity` is in range and has the component. let (component, ticks, caller) = unsafe { sparse_set @@ -2670,9 +2638,9 @@ unsafe impl ReadOnlyQueryData for PhantomData {} /// [`StorageType`] of a given component. pub(super) union StorageSwitch { /// The table variant. Requires the component to be a table component. - table: T, + table: (T, usize), /// The sparse set variant. Requires the component to be a sparse set component. - sparse_set: S, + sparse_set: (S, Entity), _marker: PhantomData, } @@ -2681,9 +2649,11 @@ impl StorageSwitch { /// the variant corresponding to the component's [`StorageType`]. pub fn new(table: impl FnOnce() -> T, sparse_set: impl FnOnce() -> S) -> Self { match C::STORAGE_TYPE { - StorageType::Table => Self { table: table() }, + StorageType::Table => Self { + table: (table(), usize::MAX), + }, StorageType::SparseSet => Self { - sparse_set: sparse_set(), + sparse_set: (sparse_set(), Entity::PLACEHOLDER), }, } } @@ -2700,7 +2670,53 @@ impl StorageSwitch { #[inline] pub unsafe fn set_table(&mut self, table: T) { match C::STORAGE_TYPE { - StorageType::Table => self.table = table, + StorageType::Table => self.table.0 = table, + _ => { + #[cfg(debug_assertions)] + unreachable!(); + #[cfg(not(debug_assertions))] + core::hint::unreachable_unchecked() + } + } + } + + /// Sets the idx mask value of table variant. + /// + /// # Panics + /// + /// This will panic on debug builds if `C` is not a table component. + /// + /// # Safety + /// + /// - `C` must be a table component. + /// - The value of `mask` must be such that for any `idx` in `0..=table.len()`, `table[idx & mask]` is valid. + #[inline] + pub unsafe fn set_table_idx_mask(&mut self, mask: usize) { + match C::STORAGE_TYPE { + StorageType::Table => self.table.1 = mask, + _ => { + #[cfg(debug_assertions)] + unreachable!(); + #[cfg(not(debug_assertions))] + core::hint::unreachable_unchecked() + } + } + } + + /// Sets the entity value of sparse set variant. + /// + /// # Panics + /// + /// This will panic on debug builds if `C` is not a sparse set component. + /// + /// # Safety + /// + /// - `C` must be a sparse set component. + /// - The value of `entity` must be either an [`Entity::PLACEHOLDER`] or a valid for the sparse set entity. + #[inline] + pub unsafe fn set_sparse_set_entity(&mut self, entity: Entity) { + match C::STORAGE_TYPE { + StorageType::SparseSet => self.sparse_set.1 = entity, _ => { #[cfg(debug_assertions)] unreachable!(); @@ -2712,16 +2728,21 @@ impl StorageSwitch { /// Fetches the internal value from the variant that corresponds to the /// component's [`StorageType`]. - pub fn extract(&self, table: impl FnOnce(T) -> R, sparse_set: impl FnOnce(S) -> R) -> R { + /// The second argument stores additional information about the position of the target in the table/sparse set: + /// - sparse set: either an override entity that should be used instead, or an [`Entity::PLACEHOLDER`], which means + /// that the entity can be accessed using normal workflow. + /// - table: a bit mask that should be `&` with the target index. + #[inline] + pub fn extract( + &self, + table: impl FnOnce(T, usize) -> R, + sparse_set: impl FnOnce(S, Entity) -> R, + ) -> R { match C::STORAGE_TYPE { - StorageType::Table => table( - // SAFETY: C::STORAGE_TYPE == StorageType::Table - unsafe { self.table }, - ), - StorageType::SparseSet => sparse_set( - // SAFETY: C::STORAGE_TYPE == StorageType::SparseSet - unsafe { self.sparse_set }, - ), + // SAFETY: C::STORAGE_TYPE == StorageType::Table + StorageType::Table => unsafe { table(self.table.0, self.table.1) }, + // SAFETY: C::STORAGE_TYPE == StorageType::SparseSet + StorageType::SparseSet => unsafe { sparse_set(self.sparse_set.0, self.sparse_set.1) }, } } } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 16cde14d886f7..c0c71527f1030 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -757,7 +757,7 @@ unsafe impl QueryFilter for Added { ) -> bool { // SAFETY: The invariants are upheld by the caller. fetch.ticks.extract( - |table| { + |table, _| { // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -765,7 +765,7 @@ unsafe impl QueryFilter for Added { tick.deref().is_newer_than(fetch.last_run, fetch.this_run) }, - |sparse_set| { + |sparse_set, _| { // SAFETY: The caller ensures `entity` is in range. let tick = unsafe { sparse_set @@ -986,7 +986,7 @@ unsafe impl QueryFilter for Changed { ) -> bool { // SAFETY: The invariants are upheld by the caller. fetch.ticks.extract( - |table| { + |table, _| { // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -994,7 +994,7 @@ unsafe impl QueryFilter for Changed { tick.deref().is_newer_than(fetch.last_run, fetch.this_run) }, - |sparse_set| { + |sparse_set, _| { // SAFETY: The caller ensures `entity` is in range. let tick = unsafe { sparse_set diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 52d37aa7b4e56..e19549c4af9b4 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -3,7 +3,7 @@ use crate::{ component::{ComponentId, Tick}, entity::{Entity, EntityEquivalent, EntitySet, UniqueEntityArray}, entity_disabling::DefaultQueryFilters, - inheritance::InheritedComponents, + inheritance::{InheritedArchetypeComponent, InheritedComponents}, prelude::FromWorld, query::{Access, FilteredAccess, QueryCombinationIter, QueryIter, QueryParIter, WorldQuery}, storage::{SparseSetIndex, TableId}, @@ -643,25 +643,16 @@ impl QueryState { archetype: &Archetype, inherited_components: &InheritedComponents, ) -> bool { - let is_matching = if archetype.has_inherited_components() { - let inherited_components = inherited_components - .archetype_inherited_components - .get(&archetype.id()) - .unwrap(); - D::matches_component_set(&self.fetch_state, &|id| { - archetype.contains(id) || inherited_components.contains_key(&id) - }) && F::matches_component_set(&self.filter_state, &|id| { - archetype.contains(id) || inherited_components.contains_key(&id) - }) && self.matches_component_set(&|id| { - archetype.contains(id) || inherited_components.contains_key(&id) - }) && !inherited_components + let is_matching = D::matches_component_set(&self.fetch_state, &|id| { + archetype.contains_with_inherited(id) + }) && F::matches_component_set(&self.filter_state, &|id| { + archetype.contains_with_inherited(id) + }) && self + .matches_component_set(&|id| archetype.contains_with_inherited(id)) + && !archetype + .inherited_components .keys() - .any(|id| self.component_access.access.has_component_write(*id)) - } else { - D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id)) - && F::matches_component_set(&self.filter_state, &|id| archetype.contains(id)) - && self.matches_component_set(&|id| archetype.contains(id)) - }; + .any(|id| self.component_access.access.has_component_write(*id)); if is_matching { let archetype_index = archetype.id().index(); @@ -721,12 +712,10 @@ impl QueryState { .get_archetype_component_id(*component_access.index()) .or_else(|| { if archetype.has_inherited_components() { - inherited_components - .archetype_inherited_components - .get(&archetype.id()) - .and_then(|map| { - map.get(component_access.index()).map(|(_, id)| *id) - }) + archetype + .inherited_components + .get(component_access.index()) + .map(InheritedArchetypeComponent::archetype_component_id) } else { None } @@ -746,22 +735,8 @@ impl QueryState { return; } - let empty_map = Default::default(); for (component_id, archetype_component_id) in - archetype.components_with_archetype_component_id().chain( - if archetype.has_inherited_components() { - inherited_components - .archetype_inherited_components - .get(&archetype.id()) - .unwrap_or(&empty_map) - } else { - &empty_map - } - .iter() - .map(|(component_id, (_, archetype_component_id))| { - (*component_id, *archetype_component_id) - }), - ) + archetype.components_with_inherited_and_archetype_component_id() { if self .component_access diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index f21ce1554bbcc..0a6b3248cb62e 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -2,6 +2,7 @@ use crate::{ change_detection::MaybeLocation, component::{ComponentId, ComponentInfo, ComponentTicks, Components, Tick}, entity::Entity, + inheritance::InheritedTableComponent, query::DebugCheckedUnwrap, storage::{blob_vec::BlobVec, ImmutableSparseSet, SparseSet}, world::INHERIT_FROM, @@ -182,6 +183,7 @@ impl TableBuilder { columns: self.columns.into_immutable(), entities: Vec::with_capacity(self.capacity), has_inherited_components: self.has_inherited_components, + inherited_components: Default::default(), } } } @@ -202,6 +204,7 @@ pub struct Table { columns: ImmutableSparseSet, entities: Vec, has_inherited_components: bool, + pub(crate) inherited_components: HashMap, } struct AbortOnPanic; diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c1f5d961a8788..8a3dc60745aa9 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -2128,24 +2128,6 @@ impl<'w> EntityWorldMut<'w> { let old_table_row = remove_result.table_row; let old_table_id = old_archetype.table_id(); - if old_table_id == archetypes[new_archetype_id].table_id() { - inherited_components.update_inherited_archetypes::( - archetypes, - components, - old_archetype_id, - new_archetype_id, - entity, - ); - } else { - inherited_components.update_inherited_archetypes::( - archetypes, - components, - old_archetype_id, - new_archetype_id, - entity, - ); - } - let new_archetype = &mut archetypes[new_archetype_id]; let new_location = if old_table_id == new_archetype.table_id() { @@ -2186,6 +2168,28 @@ impl<'w> EntityWorldMut<'w> { new_location }; + if archetypes[new_archetype_id].is_inherited() { + if old_table_id == archetypes[new_archetype_id].table_id() { + inherited_components.update_inherited_archetypes::( + archetypes, + &mut storages.tables, + new_archetype_id, + old_archetype_id, + entity, + new_location, + ); + } else { + inherited_components.update_inherited_archetypes::( + archetypes, + &mut storages.tables, + new_archetype_id, + old_archetype_id, + entity, + new_location, + ); + } + } + *self_location = new_location; // SAFETY: The entity is valid and has been moved to the new location already. unsafe { diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 91dcc6124a227..b5864e7e64603 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -7,7 +7,7 @@ use crate::{ change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, entity::{ContainsEntity, Entities, Entity, EntityDoesNotExistError, EntityLocation}, - inheritance::InheritedComponents, + inheritance::{InheritedArchetypeComponent, InheritedComponents}, observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, ReadOnlyQueryData}, @@ -986,20 +986,7 @@ impl<'w> UnsafeEntityCell<'w> { .debug_checked_unwrap() }; - let archetype_inherited_components = if archetype.has_inherited_components() { - self.world - .inherited_components() - .archetype_inherited_components - .get(&archetype.id()) - } else { - None - }; - - if Q::matches_component_set(&state, &|id| { - archetype.contains(id) - || archetype_inherited_components - .is_some_and(|components| components.contains_key(&id)) - }) { + if Q::matches_component_set(&state, &|id| archetype.contains_with_inherited(id)) { // SAFETY: state was initialized above using the world passed into this function let mut fetch = unsafe { Q::init_fetch( @@ -1231,13 +1218,26 @@ unsafe fn get_component( } .or_else(|| { let archetype = world.archetypes().get(location.archetype_id)?; - let (base, ..) = *world - .inherited_components() - .archetype_inherited_components - .get(&archetype.id())? - .get(&component_id)?; - let location = world.entities().get(base)?; - get_component(world, component_id, storage_type, base, location) + match *archetype.inherited_components.get(&component_id)? { + InheritedArchetypeComponent::Table { + table_id, + table_row, + .. + } => { + // This is fine since `fetch_table` only uses table_id + let location = EntityLocation { + table_id, + table_row, + ..location + }; + world + .fetch_table(location)? + .get_component(component_id, table_row) + } + InheritedArchetypeComponent::Sparse { entity, .. } => { + world.fetch_sparse_set(component_id)?.get(entity) + } + } }) } @@ -1292,13 +1292,42 @@ unsafe fn get_component_and_ticks( return None; } let archetype = world.archetypes().get(location.archetype_id)?; - let (base, ..) = *world - .inherited_components() - .archetype_inherited_components - .get(&archetype.id())? - .get(&component_id)?; - let location = world.entities().get(base)?; - get_component_and_ticks(world, component_id, storage_type, base, location, false) + match archetype.inherited_components.get(&component_id)? { + &InheritedArchetypeComponent::Table { + table_id, + table_row, + .. + } => { + // This is fine since archetype-related fields aren't used by later code + let location = EntityLocation { + table_id, + table_row, + ..location + }; + let table = world.fetch_table(location)?; + table + .get_component(component_id, location.table_row) + .map(|ptr| { + ( + ptr, + TickCells { + added: table + .get_added_tick(component_id, location.table_row) + .debug_checked_unwrap(), + changed: table + .get_changed_tick(component_id, location.table_row) + .debug_checked_unwrap(), + }, + table + .get_changed_by(component_id, location.table_row) + .map(|changed_by| changed_by.debug_checked_unwrap()), + ) + }) + } + &InheritedArchetypeComponent::Sparse { entity, .. } => { + world.fetch_sparse_set(component_id)?.get_with_ticks(entity) + } + } }) } @@ -1328,13 +1357,26 @@ unsafe fn get_ticks( } .or_else(|| { let archetype = world.archetypes().get(location.archetype_id)?; - let (base, ..) = *world - .inherited_components() - .archetype_inherited_components - .get(&archetype.id())? - .get(&component_id)?; - let location = world.entities().get(base)?; - get_ticks(world, component_id, storage_type, base, location) + match archetype.inherited_components.get(&component_id)? { + &InheritedArchetypeComponent::Table { + table_id, + table_row, + .. + } => { + // This is fine since `fetch_table` only uses table_id + let location = EntityLocation { + table_id, + table_row, + ..location + }; + world + .fetch_table(location)? + .get_ticks_unchecked(component_id, table_row) + } + &InheritedArchetypeComponent::Sparse { entity, .. } => { + world.fetch_sparse_set(component_id)?.get_ticks(entity) + } + } }) } From d09146c5d4705ea5ea78ad26a270444e6b01f941 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 15 Apr 2025 06:25:59 +0000 Subject: [PATCH 09/16] actually working version --- benches/benches/bevy_ecs/change_detection.rs | 3 +- crates/bevy_ecs/src/entity/entity_set.rs | 3 +- crates/bevy_ecs/src/inheritance.rs | 148 +++++- crates/bevy_ecs/src/query/fetch.rs | 513 +++++++++++++------ crates/bevy_ecs/src/query/filter.rs | 10 +- crates/bevy_ecs/src/query/state.rs | 6 +- crates/bevy_ecs/src/query/world_query.rs | 68 ++- crates/bevy_ecs/src/storage/sparse_set.rs | 1 + crates/bevy_ecs/src/storage/table/mod.rs | 2 +- crates/bevy_ecs/src/world/mod.rs | 40 ++ crates/bevy_transform/src/systems.rs | 6 +- crates/bevy_winit/src/state.rs | 4 +- 12 files changed, 600 insertions(+), 204 deletions(-) diff --git a/benches/benches/bevy_ecs/change_detection.rs b/benches/benches/bevy_ecs/change_detection.rs index 92f3251abc2b4..d72fb4593c1f4 100644 --- a/benches/benches/bevy_ecs/change_detection.rs +++ b/benches/benches/bevy_ecs/change_detection.rs @@ -187,8 +187,7 @@ fn few_changed_detection_generic + Default + let mut world = setup::(entity_count); world.clear_trackers(); let mut query = world.query::<&mut T>(); - let mut to_modify: Vec> = - query.iter_mut(&mut world).collect(); + let mut to_modify: Vec<_> = query.iter_mut(&mut world).collect(); to_modify.shuffle(&mut deterministic_rand()); for component in to_modify[0..amount_to_modify].iter_mut() { black_box(component.bench_modify()); diff --git a/crates/bevy_ecs/src/entity/entity_set.rs b/crates/bevy_ecs/src/entity/entity_set.rs index 56f597ceba931..987edb9bba279 100644 --- a/crates/bevy_ecs/src/entity/entity_set.rs +++ b/crates/bevy_ecs/src/entity/entity_set.rs @@ -479,6 +479,7 @@ impl + Debug> Debug for UniqueEntityIter mod tests { use alloc::{vec, vec::Vec}; + use crate::inheritance::MutInherited; use crate::prelude::{Schedule, World}; use crate::component::Component; @@ -522,7 +523,7 @@ mod tests { .cloned(); // With `iter_many_mut` collecting is not possible, because you need to drop each `Mut`/`&mut` before the next is retrieved. - let _results: Vec> = + let _results: Vec> = query.iter_many_unique_mut(&mut world, entity_set).collect(); } diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index a4f8c380b36b7..6311208eff3bb 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -8,19 +8,33 @@ use alloc::collections::vec_deque::VecDeque; use alloc::string::ToString; use alloc::vec::Vec; use bevy_platform_support::collections::{HashMap, HashSet}; -use core::{alloc::Layout, ptr::NonNull}; +use bevy_platform_support::sync::Mutex; +use bumpalo::Bump; +use core::mem::offset_of; +use core::{ + alloc::Layout, + cell::UnsafeCell, + ops::{Deref, DerefMut}, + panic::Location, + ptr::NonNull, +}; +use std::boxed::Box; use bevy_ptr::OwningPtr; +use crate::change_detection::TicksMut; +use crate::query::DebugCheckedUnwrap; use crate::{ archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, + change_detection::MaybeLocation, component::{ - Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, HookContext, - StorageType, + Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, ComponentInfo, + HookContext, StorageType, Tick, }, entity::{Entities, Entity, EntityHashMap, EntityLocation}, + prelude::{DetectChanges, DetectChangesMut}, storage::{TableId, TableRow, Tables}, - world::{DeferredWorld, World}, + world::{DeferredWorld, Mut, World}, }; #[derive(Component)] @@ -172,6 +186,114 @@ pub(crate) struct InheritedTableComponent { pub(crate) table_row: TableRow, } +pub(crate) struct SharedMutComponentData { + pub(crate) component_info: ComponentInfo, + pub(crate) component_ptrs: UnsafeCell>>, + pub(crate) bump: Bump, +} + +unsafe impl Send for SharedMutComponentData {} +unsafe impl Sync for SharedMutComponentData {} + +impl SharedMutComponentData { + #[cold] + #[inline(never)] + pub unsafe fn get_or_clone<'w, T>(&self, data: &'w mut T, storage_idx: usize) -> &'w mut T { + // let data = self.data_ptr.cast::().as_ref(); + self.component_ptrs + .get() + .as_mut() + .debug_checked_unwrap() + .entry(storage_idx) + .or_insert_with(|| { + let data_ptr = self.bump.alloc_layout(self.component_info.layout()); + // TODO: use clone function from component_info + core::ptr::copy_nonoverlapping(data, data_ptr.as_ptr().cast(), 1); + data_ptr + }) + .cast::() + .as_mut() + } +} + +pub struct MutInherited<'w, T> { + pub(crate) original_data: Mut<'w, T>, + pub(crate) is_inherited: bool, + pub(crate) shared_data: Option<&'w SharedMutComponentData>, + pub(crate) table_id_or_entity: usize, +} + +impl<'w, T> MutInherited<'w, T> { + pub fn ptr(&mut self) -> &mut Mut<'w, T> { + &mut self.original_data + } +} + +impl<'w, T> Deref for MutInherited<'w, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.original_data.deref() + } +} + +impl<'w, T> DerefMut for MutInherited<'w, T> { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + if self.is_inherited { + unsafe { + self.shared_data + .debug_checked_unwrap() + .get_or_clone::(self.original_data.value, self.table_id_or_entity) + } + } else { + self.original_data.deref_mut() + } + } +} + +impl<'w, T> DetectChanges for MutInherited<'w, T> { + #[inline(always)] + fn is_added(&self) -> bool { + self.original_data.is_added() + } + + #[inline(always)] + fn is_changed(&self) -> bool { + self.original_data.is_changed() + } + + #[inline(always)] + fn last_changed(&self) -> Tick { + self.original_data.last_changed() + } + + #[inline(always)] + fn changed_by(&self) -> MaybeLocation { + self.original_data.changed_by() + } +} + +impl<'w, T> DetectChangesMut for MutInherited<'w, T> { + type Inner = as DetectChangesMut>::Inner; + + #[inline(always)] + fn set_changed(&mut self) { + self.original_data.set_changed(); + } + + #[inline(always)] + fn set_last_changed(&mut self, last_changed: Tick) { + self.original_data.set_last_changed(last_changed); + } + + #[inline(always)] + fn bypass_change_detection(&mut self) -> &mut Self::Inner { + self.original_data.bypass_change_detection() + } +} + #[derive(Default)] /// Contains information about inherited components and entities. /// @@ -184,11 +306,11 @@ pub struct InheritedComponents { /// Mapping of component ids to entities representing the inherited archetypes and tables. /// Must be kept synchronized with `entities_to_ids` pub(crate) ids_to_entities: HashMap, - // /// List of inherited components along with entities containing the inherited component for the archetypes - // pub(crate) archetype_inherited_components: - // HashMap>, - // /// List of inherited components along with entities containing the inherited component for the tables - // pub(crate) table_inherited_components: HashMap>, + + pub(crate) shared_table_components: + UnsafeCell>, + pub(crate) shared_sparse_components: + UnsafeCell>, } impl InheritedComponents { @@ -732,7 +854,7 @@ mod tests { } #[test] - #[ignore = "Mutable inherited components support is not yet implemented"] + // #[ignore = "Mutable inherited components support is not yet implemented"] fn inherited_mutable() { let mut world = World::new(); @@ -744,7 +866,11 @@ mod tests { let base = world.spawn(component).id(); let inherited = world.spawn(InheritFrom(base)).id(); - let mut comp = world.get_mut::(inherited).unwrap(); + // let mut comp = world.get_mut::(inherited).unwrap(); + let mut comp = world + .query::<&mut CompA>() + .get_mut(&mut world, inherited) + .unwrap(); comp.0 = 4; world.flush(); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index a3e279e4bd8b2..779c96bead0d4 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -2,9 +2,11 @@ use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundle, change_detection::{MaybeLocation, Ticks, TicksMut}, - component::{Component, ComponentId, Components, Mutable, StorageType, Tick}, + component::{Component, ComponentId, ComponentInfo, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, - inheritance::InheritedArchetypeComponent, + inheritance::{ + InheritedArchetypeComponent, InheritedComponents, MutInherited, SharedMutComponentData, + }, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, world::{ @@ -308,6 +310,15 @@ pub unsafe trait QueryData: WorldQuery { entity: Entity, table_row: TableRow, ) -> Self::Item<'w>; + + unsafe fn fetch_inherited<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + _is_inherited: bool, + ) -> Self::Item<'w> { + Self::fetch(fetch, entity, table_row) + } } /// A [`QueryData`] that is read only. @@ -1143,6 +1154,8 @@ pub struct ReadFetch<'w, T: Component> { Option<&'w ComponentSparseSet>, >, inherited_tables: &'w Tables, + is_shared_table_component: bool, + shared_sparse_component_entity: Option, } impl Clone for ReadFetch<'_, T> { @@ -1165,7 +1178,7 @@ unsafe impl WorldQuery for &T { fetch } - #[inline] + #[inline(always)] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, @@ -1186,6 +1199,8 @@ unsafe impl WorldQuery for &T { // SAFETY: This variable will be used only to access tables that contain inherited components, which // we are allowed to access since `update_archetype_component_access` is aware of inherited components. inherited_tables: unsafe { &world.storages().tables }, + is_shared_table_component: false, + shared_sparse_component_entity: None, } } @@ -1196,7 +1211,7 @@ unsafe impl WorldQuery for &T { } }; - #[inline] + #[inline(always)] unsafe fn set_archetype<'w>( fetch: &mut ReadFetch<'w, T>, component_id: &ComponentId, @@ -1216,20 +1231,14 @@ unsafe impl WorldQuery for &T { .debug_checked_unwrap() { &InheritedArchetypeComponent::Sparse { entity, .. } => { - // SAFETY: - // - T is a SparseSet component, otherwise Self::IS_DENSE would've been true - // - Current archetype does not contain the component, therefore it must be inherited and - // must be set using the provided entity. - unsafe { - fetch.components.set_sparse_set_entity(entity); - } + fetch.shared_sparse_component_entity = Some(entity); } _ => unreachable!(), } } } - #[inline] + #[inline(always)] unsafe fn set_table<'w>( fetch: &mut ReadFetch<'w, T>, &component_id: &ComponentId, @@ -1263,7 +1272,7 @@ unsafe impl WorldQuery for &T { // and forcing fetch to access only the first element of the column for all entities. unsafe { fetch.components.set_table(table_data); - fetch.components.set_table_idx_mask(0); + fetch.is_shared_table_component = true; }; } else { let table_data = Some( @@ -1303,6 +1312,15 @@ unsafe impl WorldQuery for &T { ) -> bool { set_contains_id(state) } + + #[inline(always)] + fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { + if Self::IS_DENSE { + fetch.is_shared_table_component + } else { + fetch.shared_sparse_component_entity.is_some() + } + } } /// SAFETY: `Self` is the same as `Self::ReadOnly` @@ -1320,20 +1338,41 @@ unsafe impl QueryData for &T { fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, + ) -> Self::Item<'w> { + Self::fetch_inherited( + fetch, + entity, + table_row, + Self::has_inherited_components(fetch), + ) + } + + #[inline(always)] + unsafe fn fetch_inherited<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + is_inherited: bool, ) -> Self::Item<'w> { fetch.components.extract( - |table, mask| { + |table| { + let idx = if is_inherited { + 0 + } else { + table_row.as_usize() + }; + // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: Caller ensures `table_row` is in range. - let item = unsafe { table.get(table_row.as_usize() & mask) }; + let item = unsafe { table.get(idx) }; item.deref() }, - |sparse_set, entity_override| { - let entity = if entity_override == Entity::PLACEHOLDER { - entity + |sparse_set| { + let entity = if is_inherited { + fetch.shared_sparse_component_entity.debug_checked_unwrap() } else { - entity_override + entity }; // SAFETY: Caller ensures `entity` is in range. @@ -1370,6 +1409,8 @@ pub struct RefFetch<'w, T: Component> { last_run: Tick, this_run: Tick, inherited_tables: &'w Tables, + is_shared_table_component: bool, + shared_sparse_component_entity: Option, } impl Clone for RefFetch<'_, T> { @@ -1415,6 +1456,8 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { // SAFETY: This variable will be used only to access tables that contain inherited components, which // we are allowed to access since `update_archetype_component_access` is aware of inherited components. inherited_tables: unsafe { &world.storages().tables }, + is_shared_table_component: false, + shared_sparse_component_entity: None, } } @@ -1444,13 +1487,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { .debug_checked_unwrap() { &InheritedArchetypeComponent::Sparse { entity, .. } => { - // SAFETY: - // - T is a SparseSet component, otherwise Self::IS_DENSE would've been true - // - Current archetype does not contain the component, therefore it must be inherited and - // must be set using the provided entity. - unsafe { - fetch.components.set_sparse_set_entity(entity); - } + fetch.shared_sparse_component_entity = Some(entity); } _ => unreachable!(), } @@ -1519,7 +1556,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { // and forcing fetch to access only the first element of the column for all entities. unsafe { fetch.components.set_table(table_data); - fetch.components.set_table_idx_mask(0); + fetch.is_shared_table_component = true; }; } else { let column = table.get_column(component_id).debug_checked_unwrap(); @@ -1562,6 +1599,15 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { ) -> bool { set_contains_id(state) } + + #[inline] + fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { + if Self::IS_DENSE { + fetch.is_shared_table_component + } else { + fetch.shared_sparse_component_entity.is_some() + } + } } /// SAFETY: `Self` is the same as `Self::ReadOnly` @@ -1579,22 +1625,42 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, + ) -> Self::Item<'w> { + Self::fetch_inherited( + fetch, + entity, + table_row, + Self::has_inherited_components(fetch), + ) + } + + #[inline(always)] + unsafe fn fetch_inherited<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + is_inherited: bool, ) -> Self::Item<'w> { fetch.components.extract( - |table, mask| { + |table| { + let idx = if is_inherited { + 0 + } else { + table_row.as_usize() + }; + // SAFETY: set_table was previously called let (table_components, added_ticks, changed_ticks, callers) = unsafe { table.debug_checked_unwrap() }; - let index = table_row.as_usize() & mask; // SAFETY: The caller ensures `table_row` is in range. - let component = unsafe { table_components.get(index) }; + let component = unsafe { table_components.get(idx) }; // SAFETY: The caller ensures `table_row` is in range. - let added = unsafe { added_ticks.get(index) }; + let added = unsafe { added_ticks.get(idx) }; // SAFETY: The caller ensures `table_row` is in range. - let changed = unsafe { changed_ticks.get(index) }; + let changed = unsafe { changed_ticks.get(idx) }; // SAFETY: The caller ensures `table_row` is in range. - let caller = callers.map(|callers| unsafe { callers.get(index) }); + let caller = callers.map(|callers| unsafe { callers.get(idx) }); Ref { value: component.deref(), @@ -1607,11 +1673,11 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { changed_by: caller.map(|caller| caller.deref()), } }, - |sparse_set, entity_override| { - let entity = if entity_override == Entity::PLACEHOLDER { - entity + |sparse_set| { + let entity = if is_inherited { + fetch.shared_sparse_component_entity.debug_checked_unwrap() } else { - entity_override + entity }; // SAFETY: The caller ensures `entity` is in range and has the component. @@ -1652,6 +1718,11 @@ pub struct WriteFetch<'w, T: Component> { >, last_run: Tick, this_run: Tick, + shared_component_data: Option<&'w SharedMutComponentData>, + shared_sparse_component_entity: Option, + inherited_tables: &'w Tables, + inherited_components: &'w InheritedComponents, + component_info: &'w ComponentInfo, } impl Clone for WriteFetch<'_, T> { @@ -1674,7 +1745,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { fetch } - #[inline] + #[inline(always)] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, @@ -1694,6 +1765,14 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { ), last_run, this_run, + // SAFETY: This variable will be used only to access tables that contain inherited components, which + // we are allowed to access since `update_archetype_component_access` is aware of inherited components. + inherited_tables: unsafe { &world.storages().tables }, + + shared_component_data: None, + shared_sparse_component_entity: None, + inherited_components: world.inherited_components(), + component_info: world.components().get_info(component_id).unwrap(), } } @@ -1704,7 +1783,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { } }; - #[inline] + #[inline(always)] unsafe fn set_archetype<'w>( fetch: &mut WriteFetch<'w, T>, component_id: &ComponentId, @@ -1716,27 +1795,136 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe { Self::set_table(fetch, component_id, table, archetype.table_id()); } + } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + match archetype + .inherited_components + .get(component_id) + .debug_checked_unwrap() + { + &InheritedArchetypeComponent::Sparse { entity, .. } => { + fetch.shared_sparse_component_entity = Some(entity); + fetch.shared_component_data = Some( + fetch + .inherited_components + .shared_sparse_components + .get() + .as_mut() + .debug_checked_unwrap() + .entry((*component_id, archetype.id())) + .or_insert_with(|| SharedMutComponentData { + component_ptrs: Default::default(), + bump: Default::default(), + component_info: fetch.component_info.clone(), + }), + ); + } + _ => unreachable!(), + } } } - #[inline] + #[inline(always)] unsafe fn set_table<'w>( fetch: &mut WriteFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, - _table_id: TableId, + table_id: TableId, ) { - let column = table.get_column(component_id).debug_checked_unwrap(); - let table_data = Some(( - column.get_data_slice(table.entity_count()).into(), - column.get_added_ticks_slice(table.entity_count()).into(), - column.get_changed_ticks_slice(table.entity_count()).into(), - column - .get_changed_by_slice(table.entity_count()) - .map(Into::into), - )); - // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table - unsafe { fetch.components.set_table(table_data) }; + if table.has_inherited_components() && !table.has_column(component_id) { + set_shared_components(fetch, component_id, table, table_id); + + #[cold] + #[inline(always)] + unsafe fn set_shared_components<'w, T: Component>( + fetch: &mut WriteFetch<'w, T>, + component_id: ComponentId, + table: &'w Table, + table_id: TableId, + ) { + let info = table + .inherited_components + .get(&component_id) + .debug_checked_unwrap(); + + let table = fetch + .inherited_tables + .get(info.table_id) + .debug_checked_unwrap(); + let table_data = Some(( + core::slice::from_raw_parts::<'w, UnsafeCell>( + table + .get_component(component_id, info.table_row) + .debug_checked_unwrap() + .as_ptr() as *const _, + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_added_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_changed_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), + table + .get_changed_by(component_id, info.table_row) + .map(|loc| { + core::slice::from_raw_parts( + core::ptr::from_ref(loc.debug_checked_unwrap()), + 1, + ) + .into() + }), + )); + + // SAFETY: + // - set_table is only called when T::STORAGE_TYPE = StorageType::Table + // - Current table doesn't contain the component, therefore it must be inherited and + // so it must be set as the same component for all entities within this table. + // This is achieved by setting the table_data as the pointer to this component + // and forcing fetch to access only the first element of the column for all entities. + unsafe { + fetch.components.set_table(table_data); + fetch.shared_component_data = Some( + fetch + .inherited_components + .shared_table_components + .get() + .as_mut() + .debug_checked_unwrap() + .entry((component_id, table_id)) + .or_insert_with(|| SharedMutComponentData { + component_ptrs: Default::default(), + bump: Default::default(), + component_info: fetch.component_info.clone(), + }), + ); + }; + } + } else { + let column = table.get_column(component_id).debug_checked_unwrap(); + let table_data = Some(( + column.get_data_slice(table.entity_count()).into(), + column.get_added_ticks_slice(table.entity_count()).into(), + column.get_changed_ticks_slice(table.entity_count()).into(), + column + .get_changed_by_slice(table.entity_count()) + .map(Into::into), + )); + // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table + unsafe { fetch.components.set_table(table_data) } + }; } fn update_component_access( @@ -1765,15 +1953,20 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { ) -> bool { set_contains_id(state) } + + #[inline(always)] + fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { + fetch.shared_component_data.is_some() + } } /// SAFETY: access of `&T` is a subset of `&mut T` unsafe impl<'__w, T: Component> QueryData for &'__w mut T { const IS_READ_ONLY: bool = false; type ReadOnly = &'__w T; - type Item<'w> = Mut<'w, T>; + type Item<'w> = MutInherited<'w, T>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort>(item: MutInherited<'wlong, T>) -> MutInherited<'wshort, T> { item } @@ -1783,49 +1976,79 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - fetch.components.extract( - |table, _| { - // SAFETY: set_table was previously called - let (table_components, added_ticks, changed_ticks, callers) = - unsafe { table.debug_checked_unwrap() }; - - // SAFETY: The caller ensures `table_row` is in range. - let component = unsafe { table_components.get(table_row.as_usize()) }; - // SAFETY: The caller ensures `table_row` is in range. - let added = unsafe { added_ticks.get(table_row.as_usize()) }; - // SAFETY: The caller ensures `table_row` is in range. - let changed = unsafe { changed_ticks.get(table_row.as_usize()) }; - // SAFETY: The caller ensures `table_row` is in range. - let caller = callers.map(|callers| unsafe { callers.get(table_row.as_usize()) }); - - Mut { - value: component.deref_mut(), - ticks: TicksMut { - added: added.deref_mut(), - changed: changed.deref_mut(), - this_run: fetch.this_run, - last_run: fetch.last_run, - }, - changed_by: caller.map(|caller| caller.deref_mut()), - } - }, - |sparse_set, _| { - // SAFETY: The caller ensures `entity` is in range and has the component. - let (component, ticks, caller) = unsafe { - sparse_set - .debug_checked_unwrap() - .get_with_ticks(entity) - .debug_checked_unwrap() - }; - - Mut { - value: component.assert_unique().deref_mut(), - ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run), - changed_by: caller.map(|caller| caller.deref_mut()), - } - }, + Self::fetch_inherited( + fetch, + entity, + table_row, + Self::has_inherited_components(fetch), ) } + + #[inline(always)] + unsafe fn fetch_inherited<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + is_inherited: bool, + ) -> Self::Item<'w> { + MutInherited { + shared_data: fetch.shared_component_data, + table_id_or_entity: table_row.as_usize(), + is_inherited, + original_data: fetch.components.extract( + |table| { + let idx = if is_inherited { + 0 + } else { + table_row.as_usize() + }; + // SAFETY: set_table was previously called + let (table_components, added_ticks, changed_ticks, callers) = + unsafe { table.debug_checked_unwrap() }; + + // SAFETY: The caller ensures `table_row` is in range. + let component = unsafe { table_components.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let added = unsafe { added_ticks.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let changed = unsafe { changed_ticks.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let caller = callers.map(|callers| unsafe { callers.get(idx) }); + + Mut { + value: component.deref_mut(), + ticks: TicksMut { + added: added.deref_mut(), + changed: changed.deref_mut(), + this_run: fetch.this_run, + last_run: fetch.last_run, + }, + changed_by: caller.map(|caller| caller.deref_mut()), + } + }, + |sparse_set| { + let entity = if is_inherited { + fetch.shared_sparse_component_entity.debug_checked_unwrap() + } else { + entity + }; + // SAFETY: The caller ensures `entity` is in range and has the component. + let (component, ticks, caller) = unsafe { + sparse_set + .debug_checked_unwrap() + .get_with_ticks(entity) + .debug_checked_unwrap() + }; + + Mut { + value: component.assert_unique().deref_mut(), + ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run), + changed_by: caller.map(|caller| caller.deref_mut()), + } + }, + ), + } + } } /// When `Mut` is used in a query, it will be converted to `Ref` when transformed into its read-only form, providing access to change detection methods. @@ -1919,10 +2142,10 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> { const IS_READ_ONLY: bool = false; type ReadOnly = Ref<'__w, T>; - type Item<'w> = Mut<'w, T>; + type Item<'w> = MutInherited<'w, T>; // Forwarded to `&mut T` - fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { <&mut T as QueryData>::shrink(item) } @@ -1934,7 +2157,7 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Mut<'w, T> { + ) -> Self::Item<'w> { <&mut T as QueryData>::fetch(fetch, entity, table_row) } } @@ -2248,7 +2471,7 @@ unsafe impl ReadOnlyQueryData for Has {} pub struct AnyOf(PhantomData); macro_rules! impl_tuple_query_data { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($name: ident, $state: ident, $mask: ident)),*) => { #[expect( clippy::allow_attributes, reason = "This is a tuple-related macro; as such the lints below may not always apply." @@ -2285,9 +2508,9 @@ macro_rules! impl_tuple_query_data { entity: Entity, table_row: TableRow ) -> Self::Item<'w> { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.inner; // SAFETY: The invariants are upheld by the caller. - ($(unsafe { $name::fetch($name, entity, table_row) },)*) + ($(unsafe { $name::fetch_inherited($name, entity, table_row, fetch.inherited_filter & $mask != 0 ) },)*) } } @@ -2462,13 +2685,31 @@ macro_rules! impl_anytuple_fetch { }; } +const NUMBER: usize = 1 << 0; +const NUMBER0: usize = 1 << 0; +const NUMBER1: usize = 1 << 1; +const NUMBER2: usize = 1 << 2; +const NUMBER3: usize = 1 << 3; +const NUMBER4: usize = 1 << 4; +const NUMBER5: usize = 1 << 5; +const NUMBER6: usize = 1 << 6; +const NUMBER7: usize = 1 << 7; +const NUMBER8: usize = 1 << 8; +const NUMBER9: usize = 1 << 9; +const NUMBER10: usize = 1 << 10; +const NUMBER11: usize = 1 << 11; +const NUMBER12: usize = 1 << 12; +const NUMBER13: usize = 1 << 13; +const NUMBER14: usize = 1 << 14; + all_tuples!( #[doc(fake_variadic)] impl_tuple_query_data, 0, 15, F, - S + S, + NUMBER ); all_tuples!( #[doc(fake_variadic)] @@ -2638,9 +2879,9 @@ unsafe impl ReadOnlyQueryData for PhantomData {} /// [`StorageType`] of a given component. pub(super) union StorageSwitch { /// The table variant. Requires the component to be a table component. - table: (T, usize), + table: T, /// The sparse set variant. Requires the component to be a sparse set component. - sparse_set: (S, Entity), + sparse_set: S, _marker: PhantomData, } @@ -2649,11 +2890,9 @@ impl StorageSwitch { /// the variant corresponding to the component's [`StorageType`]. pub fn new(table: impl FnOnce() -> T, sparse_set: impl FnOnce() -> S) -> Self { match C::STORAGE_TYPE { - StorageType::Table => Self { - table: (table(), usize::MAX), - }, + StorageType::Table => Self { table: table() }, StorageType::SparseSet => Self { - sparse_set: (sparse_set(), Entity::PLACEHOLDER), + sparse_set: sparse_set(), }, } } @@ -2670,53 +2909,7 @@ impl StorageSwitch { #[inline] pub unsafe fn set_table(&mut self, table: T) { match C::STORAGE_TYPE { - StorageType::Table => self.table.0 = table, - _ => { - #[cfg(debug_assertions)] - unreachable!(); - #[cfg(not(debug_assertions))] - core::hint::unreachable_unchecked() - } - } - } - - /// Sets the idx mask value of table variant. - /// - /// # Panics - /// - /// This will panic on debug builds if `C` is not a table component. - /// - /// # Safety - /// - /// - `C` must be a table component. - /// - The value of `mask` must be such that for any `idx` in `0..=table.len()`, `table[idx & mask]` is valid. - #[inline] - pub unsafe fn set_table_idx_mask(&mut self, mask: usize) { - match C::STORAGE_TYPE { - StorageType::Table => self.table.1 = mask, - _ => { - #[cfg(debug_assertions)] - unreachable!(); - #[cfg(not(debug_assertions))] - core::hint::unreachable_unchecked() - } - } - } - - /// Sets the entity value of sparse set variant. - /// - /// # Panics - /// - /// This will panic on debug builds if `C` is not a sparse set component. - /// - /// # Safety - /// - /// - `C` must be a sparse set component. - /// - The value of `entity` must be either an [`Entity::PLACEHOLDER`] or a valid for the sparse set entity. - #[inline] - pub unsafe fn set_sparse_set_entity(&mut self, entity: Entity) { - match C::STORAGE_TYPE { - StorageType::SparseSet => self.sparse_set.1 = entity, + StorageType::Table => self.table = table, _ => { #[cfg(debug_assertions)] unreachable!(); @@ -2733,16 +2926,12 @@ impl StorageSwitch { /// that the entity can be accessed using normal workflow. /// - table: a bit mask that should be `&` with the target index. #[inline] - pub fn extract( - &self, - table: impl FnOnce(T, usize) -> R, - sparse_set: impl FnOnce(S, Entity) -> R, - ) -> R { + pub fn extract(&self, table: impl FnOnce(T) -> R, sparse_set: impl FnOnce(S) -> R) -> R { match C::STORAGE_TYPE { // SAFETY: C::STORAGE_TYPE == StorageType::Table - StorageType::Table => unsafe { table(self.table.0, self.table.1) }, + StorageType::Table => unsafe { table(self.table) }, // SAFETY: C::STORAGE_TYPE == StorageType::SparseSet - StorageType::SparseSet => unsafe { sparse_set(self.sparse_set.0, self.sparse_set.1) }, + StorageType::SparseSet => unsafe { sparse_set(self.sparse_set) }, } } } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index c0c71527f1030..34b5a918ec1f9 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -532,7 +532,7 @@ macro_rules! impl_tuple_query_filter { entity: Entity, table_row: TableRow ) -> bool { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.inner; // SAFETY: The invariants are upheld by the caller. true $(&& unsafe { $name::filter_fetch($name, entity, table_row) })* } @@ -757,7 +757,7 @@ unsafe impl QueryFilter for Added { ) -> bool { // SAFETY: The invariants are upheld by the caller. fetch.ticks.extract( - |table, _| { + |table| { // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -765,7 +765,7 @@ unsafe impl QueryFilter for Added { tick.deref().is_newer_than(fetch.last_run, fetch.this_run) }, - |sparse_set, _| { + |sparse_set| { // SAFETY: The caller ensures `entity` is in range. let tick = unsafe { sparse_set @@ -986,7 +986,7 @@ unsafe impl QueryFilter for Changed { ) -> bool { // SAFETY: The invariants are upheld by the caller. fetch.ticks.extract( - |table, _| { + |table| { // SAFETY: set_table was previously called let table = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -994,7 +994,7 @@ unsafe impl QueryFilter for Changed { tick.deref().is_newer_than(fetch.last_run, fetch.this_run) }, - |sparse_set, _| { + |sparse_set| { // SAFETY: The caller ensures `entity` is in range. let tick = unsafe { sparse_set diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index e19549c4af9b4..6a32189b8b216 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -648,11 +648,7 @@ impl QueryState { }) && F::matches_component_set(&self.filter_state, &|id| { archetype.contains_with_inherited(id) }) && self - .matches_component_set(&|id| archetype.contains_with_inherited(id)) - && !archetype - .inherited_components - .keys() - .any(|id| self.component_access.access.has_component_write(*id)); + .matches_component_set(&|id| archetype.contains_with_inherited(id)); if is_matching { let archetype_index = archetype.id().index(); diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index be62165728a47..f481b8269de6d 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use crate::{ archetype::Archetype, component::{ComponentId, Components, Tick}, @@ -136,10 +138,24 @@ pub unsafe trait WorldQuery { state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool, ) -> bool; + + fn has_inherited_components<'w>(_fetch: &Self::Fetch<'w>) -> bool { + false + } +} + +#[derive(Clone, Copy)] +pub struct TupleFetch<'w, S> +where + S: Clone, +{ + pub inherited_filter: usize, + pub inner: S, + pub phantom: PhantomData<&'w ()>, } macro_rules! impl_tuple_world_query { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($name: ident, $state: ident, $mask: ident)),*) => { #[expect( clippy::allow_attributes, @@ -164,45 +180,53 @@ macro_rules! impl_tuple_world_query { /// `update_component_access` adds all `With` and `Without` filters from the subqueries. /// This is sound because `matches_component_set` always returns `false` if any the subqueries' implementations return `false`. unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) { - type Fetch<'w> = ($($name::Fetch<'w>,)*); + type Fetch<'w> = TupleFetch<'w, ($($name::Fetch<'w>,)*)>; type State = ($($name::State,)*); + #[inline(always)] fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { - let ($($name,)*) = fetch; - ($( - $name::shrink_fetch($name), - )*) + let ($($name,)*) = fetch.inner; + TupleFetch { + inner: ($( $name::shrink_fetch($name),)*), + ..fetch + } } - #[inline] + #[inline(always)] unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { let ($($name,)*) = state; // SAFETY: The invariants are upheld by the caller. - ($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*) + TupleFetch { + inherited_filter: 0, + inner: ($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*), + phantom: Default::default(), + } } const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; - #[inline] + #[inline(always)] unsafe fn set_archetype<'w>( fetch: &mut Self::Fetch<'w>, state: &Self::State, archetype: &'w Archetype, table: &'w Table ) { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.inner; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. $(unsafe { $name::set_archetype($name, $state, archetype, table); })* + $(if $name::has_inherited_components($name) {fetch.inherited_filter &= $mask} )* } - #[inline] + #[inline(always)] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table, table_id: TableId) { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.inner; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. $(unsafe { $name::set_table($name, $state, table, table_id); })* + $(if $name::has_inherited_components($name) {fetch.inherited_filter &= $mask} )* } @@ -225,11 +249,29 @@ macro_rules! impl_tuple_world_query { }; } +const NUMBER: usize = 1 << 0; +const NUMBER0: usize = 1 << 0; +const NUMBER1: usize = 1 << 1; +const NUMBER2: usize = 1 << 2; +const NUMBER3: usize = 1 << 3; +const NUMBER4: usize = 1 << 4; +const NUMBER5: usize = 1 << 5; +const NUMBER6: usize = 1 << 6; +const NUMBER7: usize = 1 << 7; +const NUMBER8: usize = 1 << 8; +const NUMBER9: usize = 1 << 9; +const NUMBER10: usize = 1 << 10; +const NUMBER11: usize = 1 << 11; +const NUMBER12: usize = 1 << 12; +const NUMBER13: usize = 1 << 13; +const NUMBER14: usize = 1 << 14; + all_tuples!( #[doc(fake_variadic)] impl_tuple_world_query, 0, 15, F, - S + S, + NUMBER ); diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index bb79382e06a8d..2be7db54e1b7d 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -392,6 +392,7 @@ macro_rules! impl_sparse_set { /// Returns a reference to the value for `index`. /// /// Returns `None` if `index` does not have a value in the sparse set. + #[inline] pub fn get(&self, index: I) -> Option<&V> { self.sparse.get(index).map(|dense_index| { // SAFETY: if the sparse index points to something in the dense vec, it exists diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index 0a6b3248cb62e..42e61507c095e 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -644,7 +644,7 @@ impl Table { /// Returns `true` if this table has components inherited from another table. /// /// Use [`crate::inheritance::InheritedComponents`] to get get the list of inherited components. - #[inline] + #[inline(always)] pub fn has_inherited_components(&self) -> bool { self.has_inherited_components } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d73f1f7a07074..92029078b1d34 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2957,6 +2957,45 @@ impl World { self.components_registrator().apply_queued_registrations(); } + pub(crate) fn flush_inherited_mut(&mut self) { + for ((component_id, table_id), mut data) in self + .inherited_components + .shared_table_components + .get_mut() + .drain() + .collect::>() + { + unsafe { + for (table_row, component_ptr) in data.component_ptrs.get_mut() { + let entity = + self.storages().tables.get(table_id).unwrap().entities()[*table_row]; + self.entity_mut(entity) + .insert_by_id(component_id, OwningPtr::new(*component_ptr)); + } + } + } + for ((component_id, archetype_id), mut data) in self + .inherited_components + .shared_sparse_components + .get_mut() + .drain() + .collect::>() + { + unsafe { + for (entity, component_ptr) in data.component_ptrs.get_mut() { + let entity = self + .storages + .tables + .get(self.archetypes().get(archetype_id).unwrap().table_id()) + .unwrap() + .entities()[*entity]; + self.entity_mut(entity) + .insert_by_id(component_id, OwningPtr::new(*component_ptr)); + } + } + } + } + /// Flushes queued entities and commands. /// /// Queued entities will be spawned, and then commands will be applied. @@ -2964,6 +3003,7 @@ impl World { pub fn flush(&mut self) { self.flush_entities(); self.flush_components(); + self.flush_inherited_mut(); self.flush_commands(); } diff --git a/crates/bevy_transform/src/systems.rs b/crates/bevy_transform/src/systems.rs index ecf66512715e9..ad53e137be859 100644 --- a/crates/bevy_transform/src/systems.rs +++ b/crates/bevy_transform/src/systems.rs @@ -251,7 +251,9 @@ mod parallel { use crate::prelude::*; // TODO: this implementation could be used in no_std if there are equivalents of these. use alloc::{sync::Arc, vec::Vec}; - use bevy_ecs::{entity::UniqueEntityIter, prelude::*, system::lifetimeless::Read}; + use bevy_ecs::{ + entity::UniqueEntityIter, inheritance::MutInherited, prelude::*, system::lifetimeless::Read, + }; use bevy_tasks::{ComputeTaskPool, TaskPool}; use bevy_utils::Parallel; use core::sync::atomic::{AtomicI32, Ordering}; @@ -421,7 +423,7 @@ mod parallel { #[expect(unsafe_code, reason = "Mutating disjoint entities in parallel")] unsafe fn propagate_descendants_unchecked( parent: Entity, - p_global_transform: Mut, + p_global_transform: MutInherited, p_children: &Children, nodes: &NodeQuery, outbox: &mut Vec, diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index f2e2a09a6ee8f..9f8ab3dfcbf94 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -295,7 +295,7 @@ impl ApplicationHandler for WinitAppRunnerState { WindowEvent::ScaleFactorChanged { scale_factor, .. } => { react_to_scale_factor_change( window, - &mut win, + &mut win.ptr(), scale_factor, &mut window_backend_scale_factor_changed, &mut window_scale_factor_changed, @@ -954,7 +954,7 @@ pub fn winit_runner(mut app: App, event_loop: EventLoop) -> AppExit pub(crate) fn react_to_resize( window_entity: Entity, - window: &mut Mut<'_, Window>, + window: &mut bevy_ecs::inheritance::MutInherited<'_, Window>, size: PhysicalSize, window_resized: &mut EventWriter, ) { From 494793c58f771344dae63767090e440f5060d6c5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Apr 2025 12:23:06 +0000 Subject: [PATCH 10/16] finish inherited mutable components poc --- crates/bevy_ecs/src/inheritance.rs | 49 +++++----- crates/bevy_ecs/src/query/fetch.rs | 2 +- crates/bevy_ecs/src/reflect/component.rs | 18 ++-- crates/bevy_ecs/src/system/commands/mod.rs | 6 +- crates/bevy_ecs/src/world/deferred_world.rs | 3 +- crates/bevy_ecs/src/world/entity_ref.rs | 32 +++---- crates/bevy_ecs/src/world/mod.rs | 54 ++++++++--- .../bevy_ecs/src/world/unsafe_world_cell.rs | 92 +++++++++++++++---- 8 files changed, 171 insertions(+), 85 deletions(-) diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index 6311208eff3bb..bec8342b6eb63 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -20,7 +20,7 @@ use core::{ }; use std::boxed::Box; -use bevy_ptr::OwningPtr; +use bevy_ptr::{OwningPtr, PtrMut}; use crate::change_detection::TicksMut; use crate::query::DebugCheckedUnwrap; @@ -193,6 +193,9 @@ pub(crate) struct SharedMutComponentData { } unsafe impl Send for SharedMutComponentData {} +// SharedMutComponentData is NOT actually Sync, we need either a mutex or a concurrent hashmap + bumpalo-herd here. +// But this shouldn't affect performance too much since this only matters whenever an inherited mutable component +// is encountered. unsafe impl Sync for SharedMutComponentData {} impl SharedMutComponentData { @@ -220,7 +223,7 @@ pub struct MutInherited<'w, T> { pub(crate) original_data: Mut<'w, T>, pub(crate) is_inherited: bool, pub(crate) shared_data: Option<&'w SharedMutComponentData>, - pub(crate) table_id_or_entity: usize, + pub(crate) table_row: usize, } impl<'w, T> MutInherited<'w, T> { @@ -238,6 +241,13 @@ impl<'w, T> Deref for MutInherited<'w, T> { } } +impl<'w, T> AsMut for MutInherited<'w, T> { + #[inline] + fn as_mut(&mut self) -> &mut T { + self.deref_mut() + } +} + impl<'w, T> DerefMut for MutInherited<'w, T> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { @@ -245,7 +255,7 @@ impl<'w, T> DerefMut for MutInherited<'w, T> { unsafe { self.shared_data .debug_checked_unwrap() - .get_or_clone::(self.original_data.value, self.table_id_or_entity) + .get_or_clone::(self.original_data.value, self.table_row) } } else { self.original_data.deref_mut() @@ -307,6 +317,7 @@ pub struct InheritedComponents { /// Must be kept synchronized with `entities_to_ids` pub(crate) ids_to_entities: HashMap, + /// These need proper multithreading support. pub(crate) shared_table_components: UnsafeCell>, pub(crate) shared_sparse_components: @@ -718,27 +729,6 @@ mod tests { assert_eq!(query.iter(&world).len(), 2); } - #[test] - fn skip_inherited_if_mutable() { - let mut world = World::new(); - - #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)] - struct CompA(i32); - - let component = CompA(6); - let component_id = world.register_component::(); - - let base = world.spawn(component).id(); - let inherited = world.spawn(InheritFrom(base)).id(); - - assert!(world.get_mut::(inherited).is_none()); - assert!(world.get_mut_by_id(inherited, component_id).is_none()); - - let mut query = world.query::<&mut CompA>(); - assert!(query.get(&world, inherited).is_err()); - assert_eq!(query.iter(&world).len(), 1); - } - #[test] fn inherited_components_circular() { let mut world = World::new(); @@ -854,7 +844,6 @@ mod tests { } #[test] - // #[ignore = "Mutable inherited components support is not yet implemented"] fn inherited_mutable() { let mut world = World::new(); @@ -864,17 +853,21 @@ mod tests { let component = CompA(6); let base = world.spawn(component).id(); - let inherited = world.spawn(InheritFrom(base)).id(); + let inherited1 = world.spawn(InheritFrom(base)).id(); + let inherited2 = world.spawn(InheritFrom(base)).id(); // let mut comp = world.get_mut::(inherited).unwrap(); let mut comp = world .query::<&mut CompA>() - .get_mut(&mut world, inherited) + .get_mut(&mut world, inherited1) .unwrap(); comp.0 = 4; + let mut entity2 = world.get_entity_mut(inherited2).unwrap(); + let mut comp2 = entity2.get_mut::().unwrap(); + comp2.0 = 1; world.flush(); let mut query = world.query::<&CompA>(); - assert_eq!(query.iter(&world).map(|c| c.0).sum::(), 10); + assert_eq!(query.iter(&world).map(|c| c.0).sum::(), 11); } } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 779c96bead0d4..43cd2581fdee0 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1993,7 +1993,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T ) -> Self::Item<'w> { MutInherited { shared_data: fetch.shared_component_data, - table_id_or_entity: table_row.as_usize(), + table_row: table_row.as_usize(), is_inherited, original_data: fetch.components.extract( |table| { diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 893e9b13fa8e3..8d36daf94979e 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -361,12 +361,14 @@ impl FromType for ReflectComponent { panic!("Cannot call `ReflectComponent::reflect_mut` on component {name}. It is immutable, and cannot modified through reflection"); } + unimplemented!("This is not supported in this POC"); + // SAFETY: guard ensures `C` is a mutable component - unsafe { - entity - .into_mut_assume_mutable::() - .map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) - } + // unsafe { + // entity + // .into_mut_assume_mutable::() + // .map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) + // } }, reflect_unchecked_mut: |entity| { if !C::Mutability::MUTABLE { @@ -374,11 +376,13 @@ impl FromType for ReflectComponent { panic!("Cannot call `ReflectComponent::reflect_unchecked_mut` on component {name}. It is immutable, and cannot modified through reflection"); } + unimplemented!("This is not supported in this POC"); + // SAFETY: reflect_unchecked_mut is an unsafe function pointer used by // `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the component `C` on the `entity` // guard ensures `C` is a mutable component - let c = unsafe { entity.get_mut_assume_mutable::() }; - c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) + // let c = unsafe { entity.get_mut_assume_mutable::() }; + // c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) }, register_component: |world: &mut World| -> ComponentId { world.register_component::() diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 2d67c4b04a6ea..042cb2e048846 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -22,6 +22,7 @@ use crate::{ entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError}, error::{ignore, warn, BevyError, CommandWithEntity, ErrorContext, HandleError}, event::Event, + inheritance::MutInherited, observer::{Observer, TriggerTargets}, resource::Resource, schedule::ScheduleLabel, @@ -2071,7 +2072,10 @@ pub struct EntityEntryCommands<'a, T> { impl<'a, T: Component> EntityEntryCommands<'a, T> { /// Modify the component `T` if it exists, using the function `modify`. - pub fn and_modify(&mut self, modify: impl FnOnce(Mut) + Send + Sync + 'static) -> &mut Self { + pub fn and_modify( + &mut self, + modify: impl FnOnce(MutInherited) + Send + Sync + 'static, + ) -> &mut Self { self.entity_commands .queue(move |mut entity: EntityWorldMut| { if let Some(value) = entity.get_mut() { diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 02c12fe6a3560..b6d33d3083ef6 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -6,6 +6,7 @@ use crate::{ component::{ComponentId, HookContext, Mutable}, entity::Entity, event::{Event, EventId, Events, SendBatchIds}, + inheritance::MutInherited, observer::{Observers, TriggerTargets}, prelude::{Component, QueryState}, query::{QueryData, QueryFilter}, @@ -78,7 +79,7 @@ impl<'w> DeferredWorld<'w> { pub fn get_mut>( &mut self, entity: Entity, - ) -> Option> { + ) -> Option> { self.get_entity_mut(entity).ok()?.into_mut() } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 8a3dc60745aa9..b662b864aaf69 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -14,7 +14,7 @@ use crate::{ EntityLocation, }, event::Event, - inheritance::InheritedComponents, + inheritance::{InheritedComponents, MutInherited}, observer::Observer, query::{Access, ReadOnlyQueryData}, relationship::RelationshipHookMode, @@ -586,7 +586,7 @@ impl<'w> EntityMut<'w> { /// Gets mutable access to the component of type `T` for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { // SAFETY: &mut self implies exclusive access for duration of returned value unsafe { self.cell.get_mut() } } @@ -598,7 +598,7 @@ impl<'w> EntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { + pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { // SAFETY: // - &mut self implies exclusive access for duration of returned value // - Caller ensures `T` is a mutable component @@ -609,7 +609,7 @@ impl<'w> EntityMut<'w> { /// with the world `'w` lifetime for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.cell.get_mut() } } @@ -621,7 +621,7 @@ impl<'w> EntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: // - Consuming `self` implies exclusive access // - Caller ensures `T` is a mutable component @@ -1343,7 +1343,7 @@ impl<'w> EntityWorldMut<'w> { /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { self.as_mutable().into_mut() } @@ -1439,7 +1439,7 @@ impl<'w> EntityWorldMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { + pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { self.as_mutable().into_mut_assume_mutable() } @@ -1451,7 +1451,7 @@ impl<'w> EntityWorldMut<'w> { /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.into_unsafe_entity_cell().get_mut() } } @@ -1468,7 +1468,7 @@ impl<'w> EntityWorldMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.into_unsafe_entity_cell().get_mut_assume_mutable() } } @@ -3058,7 +3058,7 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 1); /// ``` #[inline] - pub fn and_modify)>(self, f: F) -> Self { + pub fn and_modify)>(self, f: F) -> Self { match self { Entry::Occupied(mut entry) => { f(entry.get_mut()); @@ -3287,7 +3287,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 17); /// ``` #[inline] - pub fn get_mut(&mut self) -> Mut<'_, T> { + pub fn get_mut(&mut self) -> MutInherited<'_, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut::().unwrap() } @@ -3316,7 +3316,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 15); /// ``` #[inline] - pub fn into_mut(self) -> Mut<'a, T> { + pub fn into_mut(self) -> MutInherited<'a, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut().unwrap() } @@ -3831,7 +3831,7 @@ impl<'w> FilteredEntityMut<'w> { /// Gets mutable access to the component of type `T` for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access .has_component_write(id) @@ -3844,7 +3844,7 @@ impl<'w> FilteredEntityMut<'w> { /// with the world `'w` lifetime for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: // - We have write access // - The bound `T: Component` ensures the component is mutable @@ -3859,7 +3859,7 @@ impl<'w> FilteredEntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access .has_component_write(id) @@ -4344,7 +4344,7 @@ where /// Returns `None` if the component doesn't have a component of that type or /// if the type is one of the excluded components. #[inline] - pub fn get_mut(&mut self) -> Option> + pub fn get_mut(&mut self) -> Option> where C: Component, { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 92029078b1d34..b7bf9ccdd40a3 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -14,7 +14,7 @@ pub mod unsafe_world_cell; #[cfg(feature = "bevy_reflect")] pub mod reflect; -use crate::inheritance::{Inherited, InheritedComponents}; +use crate::inheritance::{Inherited, InheritedComponents, MutInherited}; pub use crate::{ change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD}, world::command_queue::CommandQueue, @@ -1293,7 +1293,7 @@ impl World { pub fn get_mut>( &mut self, entity: Entity, - ) -> Option> { + ) -> Option> { self.get_entity_mut(entity).ok()?.into_mut() } @@ -2958,37 +2958,63 @@ impl World { } pub(crate) fn flush_inherited_mut(&mut self) { - for ((component_id, table_id), mut data) in self + for (component_id, entities, mut data) in self .inherited_components .shared_table_components .get_mut() .drain() .collect::>() + .into_iter() + .map(|((component_id, table_id), mut data)| { + ( + component_id, + data.component_ptrs + .get_mut() + .keys() + .map(|table_row| self.storages().tables[table_id].entities()[*table_row]) + .collect::>(), + data, + ) + }) + .collect::>() { unsafe { - for (table_row, component_ptr) in data.component_ptrs.get_mut() { - let entity = - self.storages().tables.get(table_id).unwrap().entities()[*table_row]; + for (component_ptr, entity) in data.component_ptrs.get_mut().values().zip(entities) + { self.entity_mut(entity) .insert_by_id(component_id, OwningPtr::new(*component_ptr)); } } } - for ((component_id, archetype_id), mut data) in self + for (component_id, entities, mut data) in self .inherited_components .shared_sparse_components .get_mut() .drain() .collect::>() + .into_iter() + .map(|((component_id, archetype_id), mut data)| { + ( + component_id, + data.component_ptrs + .get_mut() + .keys() + .map(|table_row| { + self.storages + .tables + .get(self.archetypes().get(archetype_id).unwrap().table_id()) + .unwrap() + .entities()[*table_row] + }) + .collect::>(), + data, + ) + }) + .collect::>() { unsafe { - for (entity, component_ptr) in data.component_ptrs.get_mut() { - let entity = self - .storages - .tables - .get(self.archetypes().get(archetype_id).unwrap().table_id()) - .unwrap() - .entities()[*entity]; + for (component_ptr, entity) in data.component_ptrs.get_mut().values().zip(entities) + { self.entity_mut(entity) .insert_by_id(component_id, OwningPtr::new(*component_ptr)); } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index b5864e7e64603..fa495d3bd2b92 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -7,7 +7,9 @@ use crate::{ change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, entity::{ContainsEntity, Entities, Entity, EntityDoesNotExistError, EntityLocation}, - inheritance::{InheritedArchetypeComponent, InheritedComponents}, + inheritance::{ + InheritedArchetypeComponent, InheritedComponents, MutInherited, SharedMutComponentData, + }, observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, ReadOnlyQueryData}, @@ -833,7 +835,7 @@ impl<'w> UnsafeEntityCell<'w> { self.location, true, ) - .map(|(value, cells, caller)| Ref { + .map(|(value, cells, caller, _, _)| Ref { // SAFETY: returned component is of type T value: value.deref::(), ticks: Ticks::from_tick_cells(cells, last_change_tick, change_tick), @@ -904,7 +906,7 @@ impl<'w> UnsafeEntityCell<'w> { /// - the [`UnsafeEntityCell`] has permission to access the component mutably /// - no other references to the component exist at the same time #[inline] - pub unsafe fn get_mut>(self) -> Option> { + pub unsafe fn get_mut>(self) -> Option> { // SAFETY: // - trait bound `T: Component` ensures component is mutable // - same safety requirements @@ -917,7 +919,7 @@ impl<'w> UnsafeEntityCell<'w> { /// - no other references to the component exist at the same time /// - the component `T` is mutable #[inline] - pub unsafe fn get_mut_assume_mutable(self) -> Option> { + pub unsafe fn get_mut_assume_mutable(self) -> Option> { // SAFETY: same safety requirements unsafe { self.get_mut_using_ticks_assume_mutable( @@ -937,7 +939,7 @@ impl<'w> UnsafeEntityCell<'w> { &self, last_change_tick: Tick, change_tick: Tick, - ) -> Option> { + ) -> Option> { self.world.assert_allows_mutable_access(); let component_id = self.world.components().get_id(TypeId::of::())?; @@ -953,14 +955,21 @@ impl<'w> UnsafeEntityCell<'w> { T::STORAGE_TYPE, self.entity, self.location, - false, + true, + ) + .map( + |(value, cells, caller, is_inherited, shared_data)| MutInherited { + original_data: Mut { + // SAFETY: returned component is of type T + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), + changed_by: caller.map(|caller| caller.deref_mut()), + }, + is_inherited, + table_row: self.location.table_row.as_usize(), + shared_data, + }, ) - .map(|(value, cells, caller)| Mut { - // SAFETY: returned component is of type T - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), - changed_by: caller.map(|caller| caller.deref_mut()), - }) } } @@ -1080,7 +1089,7 @@ impl<'w> UnsafeEntityCell<'w> { self.location, false, ) - .map(|(value, cells, caller)| MutUntyped { + .map(|(value, cells, caller, _, _)| MutUntyped { // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), ticks: TicksMut::from_tick_cells( @@ -1129,7 +1138,7 @@ impl<'w> UnsafeEntityCell<'w> { self.location, false, ) - .map(|(value, cells, caller)| MutUntyped { + .map(|(value, cells, caller, _, _)| MutUntyped { // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), ticks: TicksMut::from_tick_cells( @@ -1260,6 +1269,8 @@ unsafe fn get_component_and_ticks( Ptr<'_>, TickCells<'_>, MaybeLocation<&UnsafeCell<&'static Location<'static>>>, + bool, + Option<&SharedMutComponentData>, )> { match storage_type { StorageType::Table => { @@ -1287,6 +1298,7 @@ unsafe fn get_component_and_ticks( } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_with_ticks(entity), } + .map(|(a, b, c)| (a, b, c, false, None)) .or_else(|| { if !include_inherited { return None; @@ -1298,6 +1310,7 @@ unsafe fn get_component_and_ticks( table_row, .. } => { + let original_table_id = location.table_id; // This is fine since archetype-related fields aren't used by later code let location = EntityLocation { table_id, @@ -1321,12 +1334,57 @@ unsafe fn get_component_and_ticks( table .get_changed_by(component_id, location.table_row) .map(|changed_by| changed_by.debug_checked_unwrap()), + true, + Some( + &*world + .inherited_components() + .shared_table_components + .get() + .as_mut() + .debug_checked_unwrap() + .entry((component_id, original_table_id)) + .or_insert_with(|| SharedMutComponentData { + component_ptrs: Default::default(), + bump: Default::default(), + component_info: world + .components() + .get_info(component_id) + .unwrap() + .clone(), + }), + ), ) }) } - &InheritedArchetypeComponent::Sparse { entity, .. } => { - world.fetch_sparse_set(component_id)?.get_with_ticks(entity) - } + InheritedArchetypeComponent::Sparse { entity, .. } => world + .fetch_sparse_set(component_id)? + .get_with_ticks(*entity) + .map(|(a, b, c)| { + ( + a, + b, + c, + true, + Some( + &*world + .inherited_components() + .shared_sparse_components + .get() + .as_mut() + .debug_checked_unwrap() + .entry((component_id, archetype.id())) + .or_insert_with(|| SharedMutComponentData { + component_ptrs: Default::default(), + bump: Default::default(), + component_info: world + .components() + .get_info(component_id) + .unwrap() + .clone(), + }), + ), + ) + }), } }) } From 655d10195867f7adc51e25b8c2f736ec77e7e36f Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Apr 2025 12:48:00 +0000 Subject: [PATCH 11/16] make poc compile with everything --- crates/bevy_ecs/src/inheritance.rs | 54 ++++++++++++++++++++++++++ crates/bevy_gizmos/src/gizmos.rs | 9 ++++- crates/bevy_text/src/text_access.rs | 59 +++++++++++++++++++++++------ crates/bevy_ui/src/widget/text.rs | 11 +++--- 4 files changed, 115 insertions(+), 18 deletions(-) diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index bec8342b6eb63..b2192b15e89bc 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -186,6 +186,7 @@ pub(crate) struct InheritedTableComponent { pub(crate) table_row: TableRow, } +#[derive(Debug)] pub(crate) struct SharedMutComponentData { pub(crate) component_info: ComponentInfo, pub(crate) component_ptrs: UnsafeCell>>, @@ -219,6 +220,7 @@ impl SharedMutComponentData { } } +#[derive(Debug)] pub struct MutInherited<'w, T> { pub(crate) original_data: Mut<'w, T>, pub(crate) is_inherited: bool, @@ -230,6 +232,58 @@ impl<'w, T> MutInherited<'w, T> { pub fn ptr(&mut self) -> &mut Mut<'w, T> { &mut self.original_data } + + pub fn into_inner(mut self) -> &'w mut T { + if self.is_inherited { + unsafe { + self.shared_data + .debug_checked_unwrap() + .get_or_clone::(self.original_data.value, self.table_row) + } + } else { + self.original_data.set_changed(); + self.original_data.value + } + } + + pub fn map_unchanged(self, f: impl FnOnce(&mut T) -> &mut U) -> MutInherited<'w, U> { + if self.is_inherited { + unsafe { + let new_value = f(self + .shared_data + .debug_checked_unwrap() + .get_or_clone::(self.original_data.value, self.table_row)); + MutInherited { + original_data: Mut { + value: new_value, + ticks: self.original_data.ticks, + changed_by: self.original_data.changed_by, + }, + is_inherited: self.is_inherited, + shared_data: self.shared_data, + table_row: self.table_row, + } + } + } else { + MutInherited { + original_data: Mut { + value: f(self.original_data.value), + ticks: self.original_data.ticks, + changed_by: self.original_data.changed_by, + }, + is_inherited: self.is_inherited, + shared_data: self.shared_data, + table_row: self.table_row, + } + } + } +} + +impl<'w, T> AsRef for MutInherited<'w, T> { + #[inline] + fn as_ref(&self) -> &T { + self.deref() + } } impl<'w, T> Deref for MutInherited<'w, T> { diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index b51dd672fe07d..2c5ee8d3bd61b 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -10,6 +10,7 @@ use core::{ use bevy_color::{Color, LinearRgba}; use bevy_ecs::{ component::Tick, + inheritance::InheritedComponents, resource::Resource, system::{ Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam, @@ -208,11 +209,17 @@ where unsafe fn new_archetype( state: &mut Self::State, archetype: &bevy_ecs::archetype::Archetype, + inherited_components: &InheritedComponents, system_meta: &mut SystemMeta, ) { // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. unsafe { - GizmosState::::new_archetype(&mut state.state, archetype, system_meta); + GizmosState::::new_archetype( + &mut state.state, + archetype, + inherited_components, + system_meta, + ); }; } diff --git a/crates/bevy_text/src/text_access.rs b/crates/bevy_text/src/text_access.rs index 7aafa28ef63e2..a98f063ef10cf 100644 --- a/crates/bevy_text/src/text_access.rs +++ b/crates/bevy_text/src/text_access.rs @@ -1,6 +1,7 @@ use bevy_color::Color; use bevy_ecs::{ component::Mutable, + inheritance::MutInherited, prelude::*, system::{Query, SystemParam}, }; @@ -254,7 +255,13 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { &mut self, root_entity: Entity, index: usize, - ) -> Option<(Entity, usize, Mut, Mut, Mut)> { + ) -> Option<( + Entity, + usize, + MutInherited, + MutInherited, + MutInherited, + )> { // Root if index == 0 { let (text, font, color) = self.roots.get_mut(root_entity).ok()?; @@ -321,17 +328,25 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// Gets the text value of a text span within a text block at a specific index in the flattened span list. - pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option> { + pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option> { self.get(root_entity, index).map(|(_, _, text, ..)| text) } /// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list. - pub fn get_font(&mut self, root_entity: Entity, index: usize) -> Option> { + pub fn get_font( + &mut self, + root_entity: Entity, + index: usize, + ) -> Option> { self.get(root_entity, index).map(|(_, _, _, font, _)| font) } /// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list. - pub fn get_color(&mut self, root_entity: Entity, index: usize) -> Option> { + pub fn get_color( + &mut self, + root_entity: Entity, + index: usize, + ) -> Option> { self.get(root_entity, index) .map(|(_, _, _, _, color)| color) } @@ -339,21 +354,21 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { /// Gets the text value of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn text(&mut self, root_entity: Entity, index: usize) -> Mut { + pub fn text(&mut self, root_entity: Entity, index: usize) -> MutInherited { self.get_text(root_entity, index).unwrap() } /// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn font(&mut self, root_entity: Entity, index: usize) -> Mut { + pub fn font(&mut self, root_entity: Entity, index: usize) -> MutInherited { self.get_font(root_entity, index).unwrap() } /// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn color(&mut self, root_entity: Entity, index: usize) -> Mut { + pub fn color(&mut self, root_entity: Entity, index: usize) -> MutInherited { self.get_color(root_entity, index).unwrap() } @@ -361,7 +376,13 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { pub fn for_each( &mut self, root_entity: Entity, - mut callback: impl FnMut(Entity, usize, Mut, Mut, Mut), + mut callback: impl FnMut( + Entity, + usize, + MutInherited, + MutInherited, + MutInherited, + ), ) { self.for_each_until(root_entity, |a, b, c, d, e| { (callback)(a, b, c, d, e); @@ -370,14 +391,22 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// Invokes a callback on each span's string value in a text block, starting with the root entity. - pub fn for_each_text(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut)) { + pub fn for_each_text( + &mut self, + root_entity: Entity, + mut callback: impl FnMut(MutInherited), + ) { self.for_each(root_entity, |_, _, text, _, _| { (callback)(text); }); } /// Invokes a callback on each span's [`TextFont`] in a text block, starting with the root entity. - pub fn for_each_font(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut)) { + pub fn for_each_font( + &mut self, + root_entity: Entity, + mut callback: impl FnMut(MutInherited), + ) { self.for_each(root_entity, |_, _, _, font, _| { (callback)(font); }); @@ -387,7 +416,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { pub fn for_each_color( &mut self, root_entity: Entity, - mut callback: impl FnMut(Mut), + mut callback: impl FnMut(MutInherited), ) { self.for_each(root_entity, |_, _, _, _, color| { (callback)(color); @@ -401,7 +430,13 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { pub fn for_each_until( &mut self, root_entity: Entity, - mut callback: impl FnMut(Entity, usize, Mut, Mut, Mut) -> bool, + mut callback: impl FnMut( + Entity, + usize, + MutInherited, + MutInherited, + MutInherited, + ) -> bool, ) { // Root let Ok((text, font, color)) = self.roots.get_mut(root_entity) else { diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index 0153fa954c406..f59803b94a9fc 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -9,6 +9,7 @@ use bevy_ecs::{ change_detection::DetectChanges, component::Component, entity::Entity, + inheritance::MutInherited, query::With, reflect::ReflectComponent, system::{Query, Res, ResMut}, @@ -200,9 +201,9 @@ fn create_text_measure<'a>( spans: impl Iterator, block: Ref, text_pipeline: &mut TextPipeline, - mut content_size: Mut, - mut text_flags: Mut, - mut computed: Mut, + mut content_size: MutInherited, + mut text_flags: MutInherited, + mut computed: MutInherited, font_system: &mut CosmicFontSystem, ) { match text_pipeline.create_text_measure( @@ -297,8 +298,8 @@ fn queue_text( inverse_scale_factor: f32, block: &TextLayout, node: Ref, - mut text_flags: Mut, - text_layout_info: Mut, + mut text_flags: MutInherited, + text_layout_info: MutInherited, computed: &mut ComputedTextBlock, text_reader: &mut TextUiReader, font_system: &mut CosmicFontSystem, From 566e961699e10453f686e30b4a6bc4a0dcd8e2f9 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Wed, 16 Apr 2025 14:30:25 +0000 Subject: [PATCH 12/16] update to main --- crates/bevy_ecs/src/inheritance.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index b2192b15e89bc..3911fc6cb0bb6 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -7,22 +7,17 @@ use alloc::collections::vec_deque::VecDeque; use alloc::string::ToString; use alloc::vec::Vec; -use bevy_platform_support::collections::{HashMap, HashSet}; -use bevy_platform_support::sync::Mutex; +use bevy_platform::collections::{HashMap, HashSet}; use bumpalo::Bump; -use core::mem::offset_of; use core::{ alloc::Layout, cell::UnsafeCell, ops::{Deref, DerefMut}, - panic::Location, ptr::NonNull, }; -use std::boxed::Box; -use bevy_ptr::{OwningPtr, PtrMut}; +use bevy_ptr::OwningPtr; -use crate::change_detection::TicksMut; use crate::query::DebugCheckedUnwrap; use crate::{ archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, @@ -337,6 +332,10 @@ impl<'w, T> DetectChanges for MutInherited<'w, T> { fn changed_by(&self) -> MaybeLocation { self.original_data.changed_by() } + + fn added(&self) -> Tick { + self.original_data.added() + } } impl<'w, T> DetectChangesMut for MutInherited<'w, T> { @@ -356,6 +355,18 @@ impl<'w, T> DetectChangesMut for MutInherited<'w, T> { fn bypass_change_detection(&mut self) -> &mut Self::Inner { self.original_data.bypass_change_detection() } + + fn set_added(&mut self) { + if !self.is_inherited { + self.original_data.set_added(); + } + } + + fn set_last_added(&mut self, last_added: Tick) { + if !self.is_inherited { + self.original_data.set_last_added(last_added); + } + } } #[derive(Default)] From 1b94cd19fa96e18afe441ad9729e8e0150b7125c Mon Sep 17 00:00:00 2001 From: eugineerd Date: Thu, 24 Apr 2025 14:09:50 +0000 Subject: [PATCH 13/16] pointer-based `MutInherited` --- crates/bevy_ecs/src/inheritance.rs | 146 +++++++++--------- crates/bevy_ecs/src/query/fetch.rs | 115 +++++++------- .../bevy_ecs/src/world/unsafe_world_cell.rs | 12 +- crates/bevy_winit/src/state.rs | 5 +- 4 files changed, 139 insertions(+), 139 deletions(-) diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index 3911fc6cb0bb6..41c3e164efebf 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -13,12 +13,12 @@ use core::{ alloc::Layout, cell::UnsafeCell, ops::{Deref, DerefMut}, + panic::Location, ptr::NonNull, }; -use bevy_ptr::OwningPtr; +use bevy_ptr::{OwningPtr, UnsafeCellDeref}; -use crate::query::DebugCheckedUnwrap; use crate::{ archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, change_detection::MaybeLocation, @@ -31,6 +31,7 @@ use crate::{ storage::{TableId, TableRow, Tables}, world::{DeferredWorld, Mut, World}, }; +use crate::{change_detection::TicksMut, query::DebugCheckedUnwrap}; #[derive(Component)] #[component( @@ -215,63 +216,67 @@ impl SharedMutComponentData { } } -#[derive(Debug)] pub struct MutInherited<'w, T> { - pub(crate) original_data: Mut<'w, T>, + pub(crate) value: *mut T, + pub(crate) added: *mut Tick, + pub(crate) changed: *mut Tick, + pub(crate) last_run: Tick, + pub(crate) this_run: Tick, + pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, pub(crate) is_inherited: bool, pub(crate) shared_data: Option<&'w SharedMutComponentData>, pub(crate) table_row: usize, } impl<'w, T> MutInherited<'w, T> { - pub fn ptr(&mut self) -> &mut Mut<'w, T> { - &mut self.original_data - } - - pub fn into_inner(mut self) -> &'w mut T { - if self.is_inherited { - unsafe { - self.shared_data - .debug_checked_unwrap() - .get_or_clone::(self.original_data.value, self.table_row) - } - } else { - self.original_data.set_changed(); - self.original_data.value - } - } - - pub fn map_unchanged(self, f: impl FnOnce(&mut T) -> &mut U) -> MutInherited<'w, U> { - if self.is_inherited { - unsafe { - let new_value = f(self - .shared_data - .debug_checked_unwrap() - .get_or_clone::(self.original_data.value, self.table_row)); - MutInherited { - original_data: Mut { - value: new_value, - ticks: self.original_data.ticks, - changed_by: self.original_data.changed_by, - }, - is_inherited: self.is_inherited, - shared_data: self.shared_data, - table_row: self.table_row, - } - } - } else { - MutInherited { - original_data: Mut { - value: f(self.original_data.value), - ticks: self.original_data.ticks, - changed_by: self.original_data.changed_by, - }, - is_inherited: self.is_inherited, - shared_data: self.shared_data, - table_row: self.table_row, - } - } - } + // pub fn ptr(&mut self) -> &mut Mut<'w, T> { + // &mut self.original_data + // } + + // pub fn into_inner(mut self) -> &'w mut T { + // if self.is_inherited { + // unsafe { + // self.shared_data + // .debug_checked_unwrap() + // .get_or_clone::(self.value, self.table_row) + // } + // } else { + // self.ticks. + // self.original_data.value + // } + // } + + // pub fn map_unchanged(self, f: impl FnOnce(&mut T) -> &mut U) -> MutInherited<'w, U> { + // if self.is_inherited { + // unsafe { + // let new_value = f(self + // .shared_data + // .debug_checked_unwrap() + // .get_or_clone::(self.original_data.value, self.table_row)); + // MutInherited { + // original_data: Mut { + // value: new_value, + // ticks: self.original_data.ticks, + // changed_by: self.original_data.changed_by, + // }, + // is_inherited: self.is_inherited, + // shared_data: self.shared_data, + // table_row: self.table_row, + // } + // } + // } else { + // MutInherited { + // original_data: Mut { + // value: f(self.original_data.value), + // ticks: self.original_data.ticks, + // changed_by: self.original_data.changed_by, + // }, + // is_inherited: self.is_inherited, + // shared_data: self.shared_data, + // table_row: self.table_row, + // } + // } + // } } impl<'w, T> AsRef for MutInherited<'w, T> { @@ -286,7 +291,7 @@ impl<'w, T> Deref for MutInherited<'w, T> { #[inline(always)] fn deref(&self) -> &Self::Target { - self.original_data.deref() + unsafe { &*self.value } } } @@ -304,10 +309,11 @@ impl<'w, T> DerefMut for MutInherited<'w, T> { unsafe { self.shared_data .debug_checked_unwrap() - .get_or_clone::(self.original_data.value, self.table_row) + .get_or_clone::(&mut *self.value, self.table_row) } } else { - self.original_data.deref_mut() + self.set_changed(); + unsafe { &mut *self.value } } } } @@ -315,26 +321,26 @@ impl<'w, T> DerefMut for MutInherited<'w, T> { impl<'w, T> DetectChanges for MutInherited<'w, T> { #[inline(always)] fn is_added(&self) -> bool { - self.original_data.is_added() + false } #[inline(always)] fn is_changed(&self) -> bool { - self.original_data.is_changed() + false } #[inline(always)] fn last_changed(&self) -> Tick { - self.original_data.last_changed() + unsafe { *self.changed } } #[inline(always)] fn changed_by(&self) -> MaybeLocation { - self.original_data.changed_by() + self.changed_by.copied() } fn added(&self) -> Tick { - self.original_data.added() + unsafe { *self.added } } } @@ -343,30 +349,20 @@ impl<'w, T> DetectChangesMut for MutInherited<'w, T> { #[inline(always)] fn set_changed(&mut self) { - self.original_data.set_changed(); + unsafe { *self.changed = self.this_run }; } #[inline(always)] - fn set_last_changed(&mut self, last_changed: Tick) { - self.original_data.set_last_changed(last_changed); - } + fn set_last_changed(&mut self, last_changed: Tick) {} #[inline(always)] fn bypass_change_detection(&mut self) -> &mut Self::Inner { - self.original_data.bypass_change_detection() + self.deref_mut() } - fn set_added(&mut self) { - if !self.is_inherited { - self.original_data.set_added(); - } - } + fn set_added(&mut self) {} - fn set_last_added(&mut self, last_added: Tick) { - if !self.is_inherited { - self.original_data.set_last_added(last_added); - } - } + fn set_last_added(&mut self, last_added: Tick) {} } #[derive(Default)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 43cd2581fdee0..abb8aa085769a 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1991,63 +1991,66 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T table_row: TableRow, is_inherited: bool, ) -> Self::Item<'w> { - MutInherited { - shared_data: fetch.shared_component_data, - table_row: table_row.as_usize(), - is_inherited, - original_data: fetch.components.extract( - |table| { - let idx = if is_inherited { - 0 - } else { - table_row.as_usize() - }; - // SAFETY: set_table was previously called - let (table_components, added_ticks, changed_ticks, callers) = - unsafe { table.debug_checked_unwrap() }; - - // SAFETY: The caller ensures `table_row` is in range. - let component = unsafe { table_components.get(idx) }; - // SAFETY: The caller ensures `table_row` is in range. - let added = unsafe { added_ticks.get(idx) }; - // SAFETY: The caller ensures `table_row` is in range. - let changed = unsafe { changed_ticks.get(idx) }; - // SAFETY: The caller ensures `table_row` is in range. - let caller = callers.map(|callers| unsafe { callers.get(idx) }); - - Mut { - value: component.deref_mut(), - ticks: TicksMut { - added: added.deref_mut(), - changed: changed.deref_mut(), - this_run: fetch.this_run, - last_run: fetch.last_run, - }, - changed_by: caller.map(|caller| caller.deref_mut()), - } - }, - |sparse_set| { - let entity = if is_inherited { - fetch.shared_sparse_component_entity.debug_checked_unwrap() - } else { - entity - }; - // SAFETY: The caller ensures `entity` is in range and has the component. - let (component, ticks, caller) = unsafe { - sparse_set - .debug_checked_unwrap() - .get_with_ticks(entity) - .debug_checked_unwrap() - }; + fetch.components.extract( + |table| { + let idx = if is_inherited { + 0 + } else { + table_row.as_usize() + }; + // SAFETY: set_table was previously called + let (table_components, added_ticks, changed_ticks, callers) = + unsafe { table.debug_checked_unwrap() }; - Mut { - value: component.assert_unique().deref_mut(), - ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run), - changed_by: caller.map(|caller| caller.deref_mut()), - } - }, - ), - } + // SAFETY: The caller ensures `table_row` is in range. + let component = unsafe { table_components.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let added = unsafe { added_ticks.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let changed = unsafe { changed_ticks.get(idx) }; + // SAFETY: The caller ensures `table_row` is in range. + let caller = callers.map(|callers| unsafe { callers.get(idx) }); + + MutInherited { + // value: component.deref_mut(), + value: component.get(), + added: added.get(), + changed: changed.get(), + this_run: fetch.this_run, + last_run: fetch.last_run, + changed_by: caller.map(|caller| caller.deref_mut()), + is_inherited, + shared_data: fetch.shared_component_data, + table_row: table_row.as_usize(), + } + }, + |sparse_set| { + let entity = if is_inherited { + fetch.shared_sparse_component_entity.debug_checked_unwrap() + } else { + entity + }; + // SAFETY: The caller ensures `entity` is in range and has the component. + let (component, ticks, caller) = unsafe { + sparse_set + .debug_checked_unwrap() + .get_with_ticks(entity) + .debug_checked_unwrap() + }; + + MutInherited { + value: component.as_ptr().cast(), + added: ticks.added.get(), + changed: ticks.changed.get(), + last_run: fetch.last_run, + this_run: fetch.this_run, + changed_by: caller.map(|caller| caller.deref_mut()), + is_inherited, + shared_data: fetch.shared_component_data, + table_row: table_row.as_usize(), + } + }, + ) } } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 9df83630a7a81..7f0b050e7f649 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -959,12 +959,12 @@ impl<'w> UnsafeEntityCell<'w> { ) .map( |(value, cells, caller, is_inherited, shared_data)| MutInherited { - original_data: Mut { - // SAFETY: returned component is of type T - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), - changed_by: caller.map(|caller| caller.deref_mut()), - }, + value: value.as_ptr().cast(), + this_run: change_tick, + last_run: last_change_tick, + added: cells.added.get(), + changed: cells.changed.get(), + changed_by: caller.map(|caller| caller.deref_mut()), is_inherited, table_row: self.location.table_row.as_usize(), shared_data, diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index fe45fff296fce..ac85c350c747b 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -6,6 +6,7 @@ use bevy_ecs::{ change_detection::{DetectChanges, NonSendMut, Res}, entity::Entity, event::{EventCursor, EventWriter}, + inheritance::MutInherited, prelude::*, system::SystemState, world::FromWorld, @@ -295,7 +296,7 @@ impl ApplicationHandler for WinitAppRunnerState { WindowEvent::ScaleFactorChanged { scale_factor, .. } => { react_to_scale_factor_change( window, - &mut win.ptr(), + &mut win, scale_factor, &mut window_backend_scale_factor_changed, &mut window_scale_factor_changed, @@ -971,7 +972,7 @@ pub(crate) fn react_to_resize( pub(crate) fn react_to_scale_factor_change( window_entity: Entity, - window: &mut Mut<'_, Window>, + window: &mut MutInherited<'_, Window>, scale_factor: f64, window_backend_scale_factor_changed: &mut EventWriter, window_scale_factor_changed: &mut EventWriter, From 05ab3096c8ca974e43d4fe26589cdaab229deeb7 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 27 Apr 2025 21:03:49 +0000 Subject: [PATCH 14/16] Big rewrite for shared mutable components. Less UB, but also less performance :/ --- crates/bevy_ecs/Cargo.toml | 2 +- crates/bevy_ecs/src/bundle.rs | 7 +- crates/bevy_ecs/src/entity/entity_set.rs | 4 +- crates/bevy_ecs/src/inheritance.rs | 654 ++++++++++++++---- crates/bevy_ecs/src/query/fetch.rs | 207 +++--- crates/bevy_ecs/src/reflect/component.rs | 18 +- crates/bevy_ecs/src/system/commands/mod.rs | 4 +- crates/bevy_ecs/src/world/deferred_world.rs | 4 +- crates/bevy_ecs/src/world/entity_ref.rs | 32 +- crates/bevy_ecs/src/world/mod.rs | 74 +- .../bevy_ecs/src/world/unsafe_world_cell.rs | 85 +-- crates/bevy_text/src/text_access.rs | 42 +- crates/bevy_transform/src/systems.rs | 4 +- crates/bevy_ui/src/widget/text.rs | 12 +- crates/bevy_winit/src/state.rs | 6 +- 15 files changed, 742 insertions(+), 413 deletions(-) diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 97cdcee0824b7..66bbbb58d8042 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -130,7 +130,7 @@ indexmap = { version = "2.5.0", default-features = false } variadics_please = { version = "1.1", default-features = false } tracing = { version = "0.1", default-features = false, optional = true } log = { version = "0.4", default-features = false } -bumpalo = "3" +bumpalo = { version = "3", features = ["boxed"] } concurrent-queue = { version = "2.5.0", default-features = false } [target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies] diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index cd2ab09916205..ef66d58781bd0 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -1308,10 +1308,9 @@ impl<'w> BundleInserter<'w> { } }; - let new_archetype = &*new_archetype; + let (new_archetype_id, is_inherited) = (new_archetype.id(), new_archetype.is_inherited()); - if new_archetype.is_inherited() { - let new_archetype_id = new_location.archetype_id; + if is_inherited { let old_archetype_id = location.archetype_id; let world = self.world.world_mut(); if new_location.table_id != location.table_id { @@ -1339,6 +1338,8 @@ impl<'w> BundleInserter<'w> { } } + let new_archetype = &self.world.archetypes()[new_archetype_id]; + // SAFETY: We have no outstanding mutable references to world as they were dropped let mut deferred_world = unsafe { self.world.into_deferred() }; diff --git a/crates/bevy_ecs/src/entity/entity_set.rs b/crates/bevy_ecs/src/entity/entity_set.rs index 8fc68373077ba..a5bf4d079c1ab 100644 --- a/crates/bevy_ecs/src/entity/entity_set.rs +++ b/crates/bevy_ecs/src/entity/entity_set.rs @@ -479,7 +479,7 @@ impl + Debug> Debug for UniqueEntityIter mod tests { use alloc::{vec, vec::Vec}; - use crate::inheritance::MutInherited; + use crate::inheritance::MutComponent; use crate::prelude::{Schedule, World}; use crate::component::Component; @@ -523,7 +523,7 @@ mod tests { .cloned(); // With `iter_many_mut` collecting is not possible, because you need to drop each `Mut`/`&mut` before the next is retrieved. - let _results: Vec> = + let _results: Vec> = query.iter_many_unique_mut(&mut world, entity_set).collect(); } diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index 41c3e164efebf..4009fb6d94b30 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -4,20 +4,23 @@ //! to record and resolve archetypes/tables that contain components from an entity in another archetype/table. //! //! [`InheritFrom`] is the main user-facing component that allows some entity to inherit components from some other entity. +use alloc::boxed::Box; use alloc::collections::vec_deque::VecDeque; use alloc::string::ToString; use alloc::vec::Vec; -use bevy_platform::collections::{HashMap, HashSet}; +use bevy_platform::{ + collections::{HashMap, HashSet}, + sync::Mutex, +}; use bumpalo::Bump; use core::{ alloc::Layout, - cell::UnsafeCell, ops::{Deref, DerefMut}, panic::Location, ptr::NonNull, }; -use bevy_ptr::{OwningPtr, UnsafeCellDeref}; +use bevy_ptr::OwningPtr; use crate::{ archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, @@ -182,190 +185,450 @@ pub(crate) struct InheritedTableComponent { pub(crate) table_row: TableRow, } -#[derive(Debug)] -pub(crate) struct SharedMutComponentData { +#[derive(Clone)] +pub(crate) struct MutComponentSharedData { pub(crate) component_info: ComponentInfo, - pub(crate) component_ptrs: UnsafeCell>>, - pub(crate) bump: Bump, + pub(crate) bump: NonNull>, + pub(crate) component_ptrs: NonNull>>, } -unsafe impl Send for SharedMutComponentData {} -// SharedMutComponentData is NOT actually Sync, we need either a mutex or a concurrent hashmap + bumpalo-herd here. -// But this shouldn't affect performance too much since this only matters whenever an inherited mutable component -// is encountered. -unsafe impl Sync for SharedMutComponentData {} +impl Drop for MutComponentSharedData { + fn drop(&mut self) { + unsafe { drop(bumpalo::boxed::Box::from_raw(self.component_ptrs.as_ptr())) } + } +} -impl SharedMutComponentData { - #[cold] - #[inline(never)] - pub unsafe fn get_or_clone<'w, T>(&self, data: &'w mut T, storage_idx: usize) -> &'w mut T { - // let data = self.data_ptr.cast::().as_ref(); - self.component_ptrs - .get() - .as_mut() - .debug_checked_unwrap() - .entry(storage_idx) - .or_insert_with(|| { - let data_ptr = self.bump.alloc_layout(self.component_info.layout()); +impl MutComponentSharedData { + fn alloc( + bump: &Mutex, + bump_ptr: NonNull>, + component_info: &ComponentInfo, + ) -> NonNull { + let bump_lock = bump.lock().unwrap(); + let component_ptrs = unsafe { + NonNull::new_unchecked(bumpalo::boxed::Box::into_raw(bumpalo::boxed::Box::new_in( + Default::default(), + &bump_lock, + ))) + }; + let shared_data = bumpalo::boxed::Box::new_in( + MutComponentSharedData { + bump: NonNull::from(bump), + component_info: component_info.clone(), + component_ptrs, + }, + &bump_lock, + ); + unsafe { NonNull::new_unchecked(bumpalo::boxed::Box::into_raw(shared_data)) } + } + + unsafe fn from_ptr<'a>( + shared_data: NonNull, + ) -> bumpalo::boxed::Box<'a, MutComponentSharedData> { + unsafe { bumpalo::boxed::Box::from_raw(shared_data.as_ptr()) } + } + + fn components(&self) -> &Mutex> { + unsafe { self.component_ptrs.as_ref() } + } + + fn get_or_cloned<'w, T: Component>( + &self, + data: &'w T, + table_row: TableRow, + this_run: Tick, + caller: MaybeLocation, + ) -> ComponentMut<'w, T> { + unsafe { + let mut lock = self.component_ptrs.as_ref().lock().unwrap(); + let ptrs = lock.entry(table_row.as_usize()).or_insert_with(|| { + let bump_lock = self.bump.as_ref().lock().unwrap(); + let value = bump_lock.alloc_layout(self.component_info.layout()); + let added = bump_lock.alloc(this_run); + let changed = bump_lock.alloc(this_run); + let changed_by = caller.map(|l| NonNull::from(bump_lock.alloc(l))); // TODO: use clone function from component_info - core::ptr::copy_nonoverlapping(data, data_ptr.as_ptr().cast(), 1); - data_ptr - }) - .cast::() - .as_mut() + core::ptr::copy_nonoverlapping(data, value.as_ptr().cast(), 1); + + MutComponentPtrs { + value, + added: NonNull::from(added), + changed: NonNull::from(changed), + changed_by, + } + }); + ComponentMut { + value: ptrs.value.cast().as_mut(), + added: ptrs.added.as_mut(), + changed: ptrs.changed.as_mut(), + changed_by: ptrs.changed_by.map(|mut l| l.as_mut()), + } + } } + + #[inline(always)] + fn try_get<'w, T: Component>(&self, table_row: TableRow) -> Option> { + unsafe { + self.component_ptrs + .as_ref() + .lock() + .unwrap() + .get(&table_row.as_usize()) + .map(|ptrs| ComponentRef { + value: ptrs.value.cast().as_ref(), + added: ptrs.added.as_ref(), + changed: ptrs.changed.as_ref(), + changed_by: ptrs.changed_by.map(|l| l.as_ref()).copied(), + }) + } + } +} + +#[derive(Clone, Copy)] +pub struct MutComponentPtrs { + pub(crate) value: NonNull, + pub(crate) added: NonNull, + pub(crate) changed: NonNull, + pub(crate) changed_by: MaybeLocation>>, } -pub struct MutInherited<'w, T> { - pub(crate) value: *mut T, - pub(crate) added: *mut Tick, - pub(crate) changed: *mut Tick, +pub struct MutComponent<'w, T: Component> { + pub(crate) value: NonNull, + pub(crate) added: NonNull, + pub(crate) changed: NonNull, + pub(crate) changed_by: MaybeLocation>>, + pub(crate) last_run: Tick, pub(crate) this_run: Tick, - pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, - pub(crate) is_inherited: bool, - pub(crate) shared_data: Option<&'w SharedMutComponentData>, - pub(crate) table_row: usize, + pub(crate) is_shared: bool, + pub(crate) shared_data: Option<&'w MutComponentSharedData>, + pub(crate) table_row: TableRow, +} + +struct ComponentRef<'w, T: Component> { + value: &'w T, + added: &'w Tick, + changed: &'w Tick, + changed_by: MaybeLocation, +} + +struct ComponentMut<'w, T: Component> { + value: &'w mut T, + added: &'w mut Tick, + changed: &'w mut Tick, + changed_by: MaybeLocation<&'w mut &'static Location<'static>>, +} + +impl<'w, T: Component> MutComponent<'w, T> { + #[inline(always)] + fn dispatch(&self, func: impl FnOnce(ComponentRef<'w, T>) -> R) -> R { + let (value, added, changed, changed_by) = unsafe { + ( + self.value.cast().as_ref(), + self.added.as_ref(), + self.changed.as_ref(), + self.changed_by.map(|l| l.as_ref()).copied(), + ) + }; + + if self.is_shared { + #[cold] + #[inline(never)] + fn unlikely<'w, R, T: Component>( + func: impl FnOnce(ComponentRef<'w, T>) -> R, + shared_data: &'w MutComponentSharedData, + value: &'w T, + added: &'w Tick, + changed: &'w Tick, + changed_by: MaybeLocation, + table_row: TableRow, + ) -> R { + func(shared_data.try_get(table_row).unwrap_or(ComponentRef { + value, + added, + changed, + changed_by, + })) + } + + unlikely( + func, + unsafe { self.shared_data.debug_checked_unwrap() }, + value, + added, + changed, + changed_by, + self.table_row, + ) + } else { + func(ComponentRef { + value, + added, + changed, + changed_by, + }) + } + } + + #[track_caller] + fn dispatch_mut( + &mut self, + func: impl FnOnce(ComponentMut<'w, T>) -> R, + ) -> R { + if self.is_shared { + #[cold] + #[inline(never)] + #[track_caller] + fn unlikely<'w, R, T: Component, const CHANGE: bool>( + func: impl FnOnce(ComponentMut<'w, T>) -> R, + shared_data: &'w MutComponentSharedData, + value: &'w T, + this_run: Tick, + table_row: TableRow, + changed_by: MaybeLocation, + ) -> R { + let mut values = shared_data.get_or_cloned(value, table_row, this_run, changed_by); + if CHANGE { + values.changed_by.assign(MaybeLocation::caller()); + } + func(values) + } + let (shared_data, value, this_run, table_row, changed_by) = unsafe { + ( + self.shared_data.debug_checked_unwrap(), + self.value.cast().as_ref(), + self.this_run, + self.table_row, + self.changed_by.map(|l| l.as_ref()).copied(), + ) + }; + + unlikely::<_, _, CHANGE>(func, shared_data, value, this_run, table_row, changed_by) + } else { + let (value, added, changed, mut changed_by) = unsafe { + ( + &mut *self.value.as_ptr(), + &mut *self.added.as_ptr(), + &mut *self.changed.as_ptr(), + self.changed_by.map(|l| &mut *l.as_ptr()), + ) + }; + if CHANGE { + *changed = self.this_run; + changed_by.assign(MaybeLocation::caller()); + } + + func(ComponentMut { + value, + added, + changed, + changed_by, + }) + } + } } -impl<'w, T> MutInherited<'w, T> { - // pub fn ptr(&mut self) -> &mut Mut<'w, T> { - // &mut self.original_data - // } - - // pub fn into_inner(mut self) -> &'w mut T { - // if self.is_inherited { - // unsafe { - // self.shared_data - // .debug_checked_unwrap() - // .get_or_clone::(self.value, self.table_row) - // } - // } else { - // self.ticks. - // self.original_data.value - // } - // } - - // pub fn map_unchanged(self, f: impl FnOnce(&mut T) -> &mut U) -> MutInherited<'w, U> { - // if self.is_inherited { - // unsafe { - // let new_value = f(self - // .shared_data - // .debug_checked_unwrap() - // .get_or_clone::(self.original_data.value, self.table_row)); - // MutInherited { - // original_data: Mut { - // value: new_value, - // ticks: self.original_data.ticks, - // changed_by: self.original_data.changed_by, - // }, - // is_inherited: self.is_inherited, - // shared_data: self.shared_data, - // table_row: self.table_row, - // } - // } - // } else { - // MutInherited { - // original_data: Mut { - // value: f(self.original_data.value), - // ticks: self.original_data.ticks, - // changed_by: self.original_data.changed_by, - // }, - // is_inherited: self.is_inherited, - // shared_data: self.shared_data, - // table_row: self.table_row, - // } - // } - // } +impl<'w, T: Component> MutComponent<'w, T> { + #[track_caller] + #[inline(always)] + pub fn into_inner(mut self) -> &'w mut T { + self.dispatch_mut::(|args| args.value) + } + + #[inline(always)] + pub fn map_unchanged(mut self, f: impl FnOnce(&mut T) -> &mut U) -> Mut<'w, U> { + let last_run = self.last_run; + let this_run = self.this_run; + self.dispatch_mut::(|args| Mut { + value: f(args.value), + changed_by: args.changed_by, + ticks: TicksMut { + added: args.added, + changed: args.changed, + last_run, + this_run, + }, + }) + } + + #[inline(always)] + pub fn reborrow(&mut self) -> MutComponent<'_, T> { + MutComponent { ..*self } + } + + #[inline(always)] + pub fn filter_map_unchanged( + mut self, + f: impl FnOnce(&mut T) -> Option<&mut U>, + ) -> Option> { + let last_run = self.last_run; + let this_run = self.this_run; + self.dispatch_mut::(|args| { + f(args.value).map(|value| Mut { + value, + changed_by: args.changed_by, + ticks: TicksMut { + added: args.added, + changed: args.changed, + last_run, + this_run, + }, + }) + }) + } + + #[inline(always)] + pub fn try_map_unchanged( + mut self, + f: impl FnOnce(&mut T) -> Result<&mut U, E>, + ) -> Result, E> { + let last_run = self.last_run; + let this_run = self.this_run; + self.dispatch_mut::(|args| { + f(args.value).map(|value| Mut { + value, + changed_by: args.changed_by, + ticks: TicksMut { + added: args.added, + changed: args.changed, + last_run, + this_run, + }, + }) + }) + } + + #[inline(always)] + pub fn as_deref_mut(&mut self) -> Mut<'_, ::Target> + where + T: DerefMut, + { + self.reborrow().map_unchanged(|v| v.deref_mut()) + } + + #[inline(always)] + pub fn as_exclusive(&mut self) -> Mut<'w, T> { + let last_run = self.last_run; + let this_run = self.this_run; + self.dispatch_mut::(|args| Mut { + value: args.value, + changed_by: args.changed_by, + ticks: TicksMut { + added: args.added, + changed: args.changed, + last_run, + this_run, + }, + }) + } } -impl<'w, T> AsRef for MutInherited<'w, T> { +impl<'w, T: Component> AsRef for MutComponent<'w, T> { #[inline] fn as_ref(&self) -> &T { self.deref() } } -impl<'w, T> Deref for MutInherited<'w, T> { +impl<'w, T: Component> Deref for MutComponent<'w, T> { type Target = T; #[inline(always)] fn deref(&self) -> &Self::Target { - unsafe { &*self.value } + self.dispatch(|args| args.value) } } -impl<'w, T> AsMut for MutInherited<'w, T> { +impl<'w, T: Component> AsMut for MutComponent<'w, T> { #[inline] fn as_mut(&mut self) -> &mut T { self.deref_mut() } } -impl<'w, T> DerefMut for MutInherited<'w, T> { +impl<'w, T: Component> DerefMut for MutComponent<'w, T> { + #[track_caller] #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { - if self.is_inherited { - unsafe { - self.shared_data - .debug_checked_unwrap() - .get_or_clone::(&mut *self.value, self.table_row) - } - } else { - self.set_changed(); - unsafe { &mut *self.value } - } + self.dispatch_mut::(|args| args.value) } } -impl<'w, T> DetectChanges for MutInherited<'w, T> { +impl<'w, T: Component> DetectChanges for MutComponent<'w, T> { #[inline(always)] fn is_added(&self) -> bool { - false + self.dispatch(|args| args.added.is_newer_than(self.last_run, self.this_run)) } #[inline(always)] fn is_changed(&self) -> bool { - false + self.dispatch(|args| args.changed.is_newer_than(self.last_run, self.this_run)) } #[inline(always)] fn last_changed(&self) -> Tick { - unsafe { *self.changed } + self.dispatch(|args| *args.changed) } #[inline(always)] fn changed_by(&self) -> MaybeLocation { - self.changed_by.copied() + self.dispatch(|args| args.changed_by) } + #[inline(always)] fn added(&self) -> Tick { - unsafe { *self.added } + self.dispatch(|args| *args.added) } } -impl<'w, T> DetectChangesMut for MutInherited<'w, T> { +impl<'w, T: Component> DetectChangesMut for MutComponent<'w, T> { type Inner = as DetectChangesMut>::Inner; #[inline(always)] + #[track_caller] fn set_changed(&mut self) { - unsafe { *self.changed = self.this_run }; + self.dispatch_mut::(|_| {}); } #[inline(always)] - fn set_last_changed(&mut self, last_changed: Tick) {} + #[track_caller] + fn set_last_changed(&mut self, last_changed: Tick) { + self.dispatch_mut::(|args| *args.changed = last_changed); + } #[inline(always)] fn bypass_change_detection(&mut self) -> &mut Self::Inner { - self.deref_mut() + self.dispatch_mut::(|args| args.value) } - fn set_added(&mut self) {} + #[inline(always)] + #[track_caller] + fn set_added(&mut self) { + let this_run = self.this_run; + self.dispatch_mut::(|args| *args.added = this_run); + } - fn set_last_added(&mut self, last_added: Tick) {} + #[inline(always)] + #[track_caller] + fn set_last_added(&mut self, last_added: Tick) { + self.dispatch_mut::(|args| { + *args.added = last_added; + *args.changed = last_added; + }); + } +} + +impl<'w, T: Component> core::fmt::Debug for MutComponent<'w, T> +where + T: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple(stringify!(MutComponent)) + .field(self.dispatch(|original| original.value)) + .finish() + } } -#[derive(Default)] /// Contains information about inherited components and entities. /// /// If an archetype or a table has inherited components, this will contain @@ -378,14 +641,34 @@ pub struct InheritedComponents { /// Must be kept synchronized with `entities_to_ids` pub(crate) ids_to_entities: HashMap, - /// These need proper multithreading support. + pub(crate) queued_shared_mutations_level: usize, pub(crate) shared_table_components: - UnsafeCell>, + Mutex>>, pub(crate) shared_sparse_components: - UnsafeCell>, + Mutex>>, + pub(crate) shared_components_bump: NonNull>, +} + +impl Default for InheritedComponents { + fn default() -> Self { + Self { + entities_to_ids: Default::default(), + ids_to_entities: Default::default(), + queued_shared_mutations_level: Default::default(), + shared_table_components: Default::default(), + shared_sparse_components: Default::default(), + shared_components_bump: unsafe { + NonNull::new_unchecked(Box::into_raw(Box::default())) + }, + } + } } impl InheritedComponents { + fn shared_components_bump(&self) -> &Mutex { + unsafe { self.shared_components_bump.as_ref() } + } + /// This method must be called after a new archetype is created to initialized inherited components once. pub(crate) fn init_inherited_components( &mut self, @@ -614,6 +897,143 @@ impl InheritedComponents { } } } + + pub(crate) fn apply_queued_shared_mutations(world: &mut World) { + world.inherited_components.queued_shared_mutations_level += 1; + + let mut queue: EntityHashMap<(Vec, Vec)> = + EntityHashMap::default(); + + for ((component_id, table_id), shared_data) in world + .inherited_components + .shared_table_components + .lock() + .unwrap() + .drain() + { + let shared_data = unsafe { MutComponentSharedData::from_ptr(shared_data) }; + let components = shared_data.components().lock().unwrap(); + for (table_row, ptrs) in components.iter() { + let entity = world.storages.tables[table_id].entities()[*table_row]; + let (components_queue, ptrs_queue, ..) = queue + .entry(entity) + .or_insert_with(|| (Vec::default(), Vec::default())); + components_queue.push(component_id); + ptrs_queue.push(*ptrs); + } + } + + for ((component_id, archetype_id), shared_data) in world + .inherited_components + .shared_sparse_components + .lock() + .unwrap() + .drain() + { + let shared_data = unsafe { MutComponentSharedData::from_ptr(shared_data) }; + let components = shared_data.components().lock().unwrap(); + for (table_row, ptrs) in components.iter() { + let entity = world.archetypes[archetype_id].entities()[*table_row].id(); + let (components_queue, ptrs_queue, ..) = queue + .entry(entity) + .or_insert_with(|| (Vec::default(), Vec::default())); + components_queue.push(component_id); + ptrs_queue.push(*ptrs); + } + } + + for (entity, (component_ids, component_ptrs)) in queue.drain() { + unsafe { + world.entity_mut(entity).insert_by_ids( + &component_ids, + component_ptrs.iter().map(|ptrs| OwningPtr::new(ptrs.value)), + ); + + // Fixup change detection fields after the fact since BundleInserter can't set them on per-component basis. + // This means that these fields might be inconsistent when observed inside hooks and observers. + for (ptrs, component_id) in component_ptrs.iter().zip(component_ids.iter()) { + let mut entity = world.entity_mut(entity); + let Ok(mut component) = entity.get_mut_by_id(*component_id) else { + continue; + }; + *component.ticks.added = ptrs.added.read(); + *component.ticks.changed = ptrs.changed.read(); + component + .changed_by + .assign(ptrs.changed_by.map(|l| l.read())); + } + } + } + + // entity_mut.insert_by_ids calls flush, which might reenter this function. + // To prevent clearing bump before all components are written, we keep track of + // recursion level and clear only on the most outer level. + world.inherited_components.queued_shared_mutations_level -= 1; + if world.inherited_components.queued_shared_mutations_level == 0 { + world + .inherited_components + .shared_components_bump() + .lock() + .unwrap() + .reset(); + } + } + + #[cold] + #[inline(always)] + pub(crate) fn get_shared_table_component_data<'w>( + &'w self, + component_info: &ComponentInfo, + table_id: TableId, + ) -> &'w MutComponentSharedData { + let mut lock = self.shared_table_components.lock().unwrap(); + let ptr = lock + .entry((component_info.id(), table_id)) + .or_insert_with(|| { + MutComponentSharedData::alloc( + self.shared_components_bump(), + self.shared_components_bump, + component_info, + ) + }); + unsafe { ptr.as_ref() } + } + + #[cold] + #[inline(always)] + pub(crate) fn get_shared_sparse_component_data<'w>( + &'w self, + component_info: &ComponentInfo, + archetype_id: ArchetypeId, + ) -> &'w MutComponentSharedData { + let mut lock = self.shared_sparse_components.lock().unwrap(); + let ptr = lock + .entry((component_info.id(), archetype_id)) + .or_insert_with(|| { + MutComponentSharedData::alloc( + self.shared_components_bump(), + self.shared_components_bump, + component_info, + ) + }); + unsafe { ptr.as_ref() } + } +} + +impl Drop for InheritedComponents { + fn drop(&mut self) { + self.shared_table_components + .lock() + .unwrap() + .values() + .chain(self.shared_sparse_components.lock().unwrap().values()) + .for_each(|ptr| unsafe { + drop(MutComponentSharedData::from_ptr(*ptr)); + }); + unsafe { + drop(Box::from_raw(self.shared_components_bump.as_ptr())); + } + } } #[cfg(test)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index abb8aa085769a..17cb0fa43e767 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -5,7 +5,8 @@ use crate::{ component::{Component, ComponentId, ComponentInfo, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, inheritance::{ - InheritedArchetypeComponent, InheritedComponents, MutInherited, SharedMutComponentData, + InheritedArchetypeComponent, InheritedComponents, MutComponent, MutComponentPtrs, + MutComponentSharedData, }, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, @@ -15,8 +16,12 @@ use crate::{ }, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; -use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; +use core::{ + cell::UnsafeCell, hint::unreachable_unchecked, marker::PhantomData, panic::Location, + ptr::NonNull, +}; use smallvec::SmallVec; +use std::boxed::Box; use variadics_please::all_tuples; /// Types that can be fetched from a [`World`] using a [`Query`]. @@ -1718,7 +1723,7 @@ pub struct WriteFetch<'w, T: Component> { >, last_run: Tick, this_run: Tick, - shared_component_data: Option<&'w SharedMutComponentData>, + shared_component_data: Option<&'w MutComponentSharedData>, shared_sparse_component_entity: Option, inherited_tables: &'w Tables, inherited_components: &'w InheritedComponents, @@ -1806,16 +1811,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { fetch.shared_component_data = Some( fetch .inherited_components - .shared_sparse_components - .get() - .as_mut() - .debug_checked_unwrap() - .entry((*component_id, archetype.id())) - .or_insert_with(|| SharedMutComponentData { - component_ptrs: Default::default(), - bump: Default::default(), - component_info: fetch.component_info.clone(), - }), + .get_shared_sparse_component_data(fetch.component_info, archetype.id()), ); } _ => unreachable!(), @@ -1831,87 +1827,78 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { table_id: TableId, ) { if table.has_inherited_components() && !table.has_column(component_id) { - set_shared_components(fetch, component_id, table, table_id); + // set_shared_components(fetch, component_id, table, table_id); + + // #[cold] + // #[inline(always)] + // unsafe fn set_shared_components<'w, T: Component>( + // fetch: &mut WriteFetch<'w, T>, + // component_id: ComponentId, + // table: &'w Table, + // table_id: TableId, + // ) { + let info = table + .inherited_components + .get(&component_id) + .debug_checked_unwrap(); - #[cold] - #[inline(always)] - unsafe fn set_shared_components<'w, T: Component>( - fetch: &mut WriteFetch<'w, T>, - component_id: ComponentId, - table: &'w Table, - table_id: TableId, - ) { - let info = table - .inherited_components - .get(&component_id) - .debug_checked_unwrap(); - - let table = fetch - .inherited_tables - .get(info.table_id) - .debug_checked_unwrap(); - let table_data = Some(( - core::slice::from_raw_parts::<'w, UnsafeCell>( - table - .get_component(component_id, info.table_row) - .debug_checked_unwrap() - .as_ptr() as *const _, - 1, - ) - .into(), - core::slice::from_raw_parts( - core::ptr::from_ref( - table - .get_added_tick(component_id, info.table_row) - .debug_checked_unwrap(), - ), - 1, - ) - .into(), - core::slice::from_raw_parts( - core::ptr::from_ref( - table - .get_changed_tick(component_id, info.table_row) - .debug_checked_unwrap(), - ), - 1, - ) - .into(), + let table = fetch + .inherited_tables + .get(info.table_id) + .debug_checked_unwrap(); + let table_data = Some(( + core::slice::from_raw_parts::<'w, UnsafeCell>( table - .get_changed_by(component_id, info.table_row) - .map(|loc| { - core::slice::from_raw_parts( - core::ptr::from_ref(loc.debug_checked_unwrap()), - 1, - ) - .into() - }), - )); - - // SAFETY: - // - set_table is only called when T::STORAGE_TYPE = StorageType::Table - // - Current table doesn't contain the component, therefore it must be inherited and - // so it must be set as the same component for all entities within this table. - // This is achieved by setting the table_data as the pointer to this component - // and forcing fetch to access only the first element of the column for all entities. - unsafe { - fetch.components.set_table(table_data); - fetch.shared_component_data = Some( - fetch - .inherited_components - .shared_table_components - .get() - .as_mut() - .debug_checked_unwrap() - .entry((component_id, table_id)) - .or_insert_with(|| SharedMutComponentData { - component_ptrs: Default::default(), - bump: Default::default(), - component_info: fetch.component_info.clone(), - }), - ); - }; - } + .get_component(component_id, info.table_row) + .debug_checked_unwrap() + .as_ptr() as *const _, + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_added_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), + core::slice::from_raw_parts( + core::ptr::from_ref( + table + .get_changed_tick(component_id, info.table_row) + .debug_checked_unwrap(), + ), + 1, + ) + .into(), + table + .get_changed_by(component_id, info.table_row) + .map(|loc| { + core::slice::from_raw_parts( + core::ptr::from_ref(loc.debug_checked_unwrap()), + 1, + ) + .into() + }), + )); + + // SAFETY: + // - set_table is only called when T::STORAGE_TYPE = StorageType::Table + // - Current table doesn't contain the component, therefore it must be inherited and + // so it must be set as the same component for all entities within this table. + // This is achieved by setting the table_data as the pointer to this component + // and forcing fetch to access only the first element of the column for all entities. + unsafe { + fetch.components.set_table(table_data); + fetch.shared_component_data = Some( + fetch + .inherited_components + .get_shared_table_component_data(fetch.component_info, table_id), + ); + }; + // } } else { let column = table.get_column(component_id).debug_checked_unwrap(); let table_data = Some(( @@ -1964,9 +1951,9 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe impl<'__w, T: Component> QueryData for &'__w mut T { const IS_READ_ONLY: bool = false; type ReadOnly = &'__w T; - type Item<'w> = MutInherited<'w, T>; + type Item<'w> = MutComponent<'w, T>; - fn shrink<'wlong: 'wshort, 'wshort>(item: MutInherited<'wlong, T>) -> MutInherited<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort>(item: MutComponent<'wlong, T>) -> MutComponent<'wshort, T> { item } @@ -2011,17 +1998,16 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T // SAFETY: The caller ensures `table_row` is in range. let caller = callers.map(|callers| unsafe { callers.get(idx) }); - MutInherited { - // value: component.deref_mut(), - value: component.get(), - added: added.get(), - changed: changed.get(), + MutComponent { + value: NonNull::new_unchecked(component.get()), + added: NonNull::new_unchecked(added.get()), + changed: NonNull::new_unchecked(changed.get()), + changed_by: caller.map(|caller| NonNull::new_unchecked(caller.get())), this_run: fetch.this_run, last_run: fetch.last_run, - changed_by: caller.map(|caller| caller.deref_mut()), - is_inherited, + is_shared: is_inherited, shared_data: fetch.shared_component_data, - table_row: table_row.as_usize(), + table_row, } }, |sparse_set| { @@ -2030,6 +2016,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T } else { entity }; + // SAFETY: The caller ensures `entity` is in range and has the component. let (component, ticks, caller) = unsafe { sparse_set @@ -2038,16 +2025,16 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T .debug_checked_unwrap() }; - MutInherited { - value: component.as_ptr().cast(), - added: ticks.added.get(), - changed: ticks.changed.get(), + MutComponent { + value: NonNull::new_unchecked(component.as_ptr().cast()), + added: NonNull::new_unchecked(ticks.added.get()), + changed: NonNull::new_unchecked(ticks.changed.get()), + changed_by: caller.map(|caller| NonNull::new_unchecked(caller.get())), last_run: fetch.last_run, this_run: fetch.this_run, - changed_by: caller.map(|caller| caller.deref_mut()), - is_inherited, + is_shared: is_inherited, shared_data: fetch.shared_component_data, - table_row: table_row.as_usize(), + table_row, } }, ) @@ -2145,7 +2132,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> { const IS_READ_ONLY: bool = false; type ReadOnly = Ref<'__w, T>; - type Item<'w> = MutInherited<'w, T>; + type Item<'w> = MutComponent<'w, T>; // Forwarded to `&mut T` fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 8d36daf94979e..893e9b13fa8e3 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -361,14 +361,12 @@ impl FromType for ReflectComponent { panic!("Cannot call `ReflectComponent::reflect_mut` on component {name}. It is immutable, and cannot modified through reflection"); } - unimplemented!("This is not supported in this POC"); - // SAFETY: guard ensures `C` is a mutable component - // unsafe { - // entity - // .into_mut_assume_mutable::() - // .map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) - // } + unsafe { + entity + .into_mut_assume_mutable::() + .map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) + } }, reflect_unchecked_mut: |entity| { if !C::Mutability::MUTABLE { @@ -376,13 +374,11 @@ impl FromType for ReflectComponent { panic!("Cannot call `ReflectComponent::reflect_unchecked_mut` on component {name}. It is immutable, and cannot modified through reflection"); } - unimplemented!("This is not supported in this POC"); - // SAFETY: reflect_unchecked_mut is an unsafe function pointer used by // `reflect_unchecked_mut` which must be called with an UnsafeEntityCell with access to the component `C` on the `entity` // guard ensures `C` is a mutable component - // let c = unsafe { entity.get_mut_assume_mutable::() }; - // c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) + let c = unsafe { entity.get_mut_assume_mutable::() }; + c.map(|c| c.map_unchanged(|value| value as &mut dyn Reflect)) }, register_component: |world: &mut World| -> ComponentId { world.register_component::() diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index b4299127fa28d..9ea0b4c007a76 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -22,7 +22,7 @@ use crate::{ entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError}, error::{ignore, warn, BevyError, CommandWithEntity, ErrorContext, HandleError}, event::Event, - inheritance::MutInherited, + inheritance::MutComponent, observer::{Observer, TriggerTargets}, resource::Resource, schedule::ScheduleLabel, @@ -2083,7 +2083,7 @@ impl<'a, T: Component> EntityEntryCommands<'a, T> { /// Modify the component `T` if it exists, using the function `modify`. pub fn and_modify( &mut self, - modify: impl FnOnce(MutInherited) + Send + Sync + 'static, + modify: impl FnOnce(MutComponent) + Send + Sync + 'static, ) -> &mut Self { self.entity_commands .queue(move |mut entity: EntityWorldMut| { diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index b6d33d3083ef6..a5021442d52e5 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -6,7 +6,7 @@ use crate::{ component::{ComponentId, HookContext, Mutable}, entity::Entity, event::{Event, EventId, Events, SendBatchIds}, - inheritance::MutInherited, + inheritance::MutComponent, observer::{Observers, TriggerTargets}, prelude::{Component, QueryState}, query::{QueryData, QueryFilter}, @@ -79,7 +79,7 @@ impl<'w> DeferredWorld<'w> { pub fn get_mut>( &mut self, entity: Entity, - ) -> Option> { + ) -> Option> { self.get_entity_mut(entity).ok()?.into_mut() } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index f8310ada58b03..4a60feb75945a 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -14,7 +14,7 @@ use crate::{ EntityLocation, }, event::Event, - inheritance::{InheritedComponents, MutInherited}, + inheritance::{InheritedComponents, MutComponent}, observer::Observer, query::{Access, ReadOnlyQueryData}, relationship::RelationshipHookMode, @@ -586,7 +586,7 @@ impl<'w> EntityMut<'w> { /// Gets mutable access to the component of type `T` for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { // SAFETY: &mut self implies exclusive access for duration of returned value unsafe { self.cell.get_mut() } } @@ -598,7 +598,7 @@ impl<'w> EntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { + pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { // SAFETY: // - &mut self implies exclusive access for duration of returned value // - Caller ensures `T` is a mutable component @@ -609,7 +609,7 @@ impl<'w> EntityMut<'w> { /// with the world `'w` lifetime for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.cell.get_mut() } } @@ -621,7 +621,7 @@ impl<'w> EntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: // - Consuming `self` implies exclusive access // - Caller ensures `T` is a mutable component @@ -1343,7 +1343,7 @@ impl<'w> EntityWorldMut<'w> { /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { self.as_mutable().into_mut() } @@ -1439,7 +1439,7 @@ impl<'w> EntityWorldMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { + pub unsafe fn get_mut_assume_mutable(&mut self) -> Option> { self.as_mutable().into_mut_assume_mutable() } @@ -1451,7 +1451,7 @@ impl<'w> EntityWorldMut<'w> { /// /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.into_unsafe_entity_cell().get_mut() } } @@ -1468,7 +1468,7 @@ impl<'w> EntityWorldMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { // SAFETY: consuming `self` implies exclusive access unsafe { self.into_unsafe_entity_cell().get_mut_assume_mutable() } } @@ -3058,7 +3058,7 @@ impl<'w, 'a, T: Component> Entry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 1); /// ``` #[inline] - pub fn and_modify)>(self, f: F) -> Self { + pub fn and_modify)>(self, f: F) -> Self { match self { Entry::Occupied(mut entry) => { f(entry.get_mut()); @@ -3287,7 +3287,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 17); /// ``` #[inline] - pub fn get_mut(&mut self) -> MutInherited<'_, T> { + pub fn get_mut(&mut self) -> MutComponent<'_, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut::().unwrap() } @@ -3316,7 +3316,7 @@ impl<'w, 'a, T: Component> OccupiedEntry<'w, 'a, T> { /// assert_eq!(world.query::<&Comp>().single(&world).unwrap().0, 15); /// ``` #[inline] - pub fn into_mut(self) -> MutInherited<'a, T> { + pub fn into_mut(self) -> MutComponent<'a, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut().unwrap() } @@ -3831,7 +3831,7 @@ impl<'w> FilteredEntityMut<'w> { /// Gets mutable access to the component of type `T` for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn get_mut>(&mut self) -> Option> { + pub fn get_mut>(&mut self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access .has_component_write(id) @@ -3844,7 +3844,7 @@ impl<'w> FilteredEntityMut<'w> { /// with the world `'w` lifetime for the current entity. /// Returns `None` if the entity does not have a component of type `T`. #[inline] - pub fn into_mut>(self) -> Option> { + pub fn into_mut>(self) -> Option> { // SAFETY: // - We have write access // - The bound `T: Component` ensures the component is mutable @@ -3859,7 +3859,7 @@ impl<'w> FilteredEntityMut<'w> { /// /// - `T` must be a mutable component #[inline] - pub unsafe fn into_mut_assume_mutable(self) -> Option> { + pub unsafe fn into_mut_assume_mutable(self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access .has_component_write(id) @@ -4344,7 +4344,7 @@ where /// Returns `None` if the component doesn't have a component of that type or /// if the type is one of the excluded components. #[inline] - pub fn get_mut(&mut self) -> Option> + pub fn get_mut(&mut self) -> Option> where C: Component, { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 502c3be1d57c2..2d29690c81537 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -14,11 +14,14 @@ pub mod unsafe_world_cell; #[cfg(feature = "bevy_reflect")] pub mod reflect; -use crate::inheritance::{Inherited, InheritedComponents, MutInherited}; pub use crate::{ change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD}, world::command_queue::CommandQueue, }; +use crate::{ + inheritance::{Inherited, InheritedComponents, MutComponent}, + prelude::DetectChangesMut, +}; pub use bevy_ecs_macros::FromWorld; pub use component_constants::*; pub use deferred_world::DeferredWorld; @@ -1293,7 +1296,7 @@ impl World { pub fn get_mut>( &mut self, entity: Entity, - ) -> Option> { + ) -> Option> { self.get_entity_mut(entity).ok()?.into_mut() } @@ -2957,69 +2960,8 @@ impl World { self.components_registrator().apply_queued_registrations(); } - pub(crate) fn flush_inherited_mut(&mut self) { - for (component_id, entities, mut data) in self - .inherited_components - .shared_table_components - .get_mut() - .drain() - .collect::>() - .into_iter() - .map(|((component_id, table_id), mut data)| { - ( - component_id, - data.component_ptrs - .get_mut() - .keys() - .map(|table_row| self.storages().tables[table_id].entities()[*table_row]) - .collect::>(), - data, - ) - }) - .collect::>() - { - unsafe { - for (component_ptr, entity) in data.component_ptrs.get_mut().values().zip(entities) - { - self.entity_mut(entity) - .insert_by_id(component_id, OwningPtr::new(*component_ptr)); - } - } - } - for (component_id, entities, mut data) in self - .inherited_components - .shared_sparse_components - .get_mut() - .drain() - .collect::>() - .into_iter() - .map(|((component_id, archetype_id), mut data)| { - ( - component_id, - data.component_ptrs - .get_mut() - .keys() - .map(|table_row| { - self.storages - .tables - .get(self.archetypes().get(archetype_id).unwrap().table_id()) - .unwrap() - .entities()[*table_row] - }) - .collect::>(), - data, - ) - }) - .collect::>() - { - unsafe { - for (component_ptr, entity) in data.component_ptrs.get_mut().values().zip(entities) - { - self.entity_mut(entity) - .insert_by_id(component_id, OwningPtr::new(*component_ptr)); - } - } - } + pub(crate) fn flush_shared_mutations(&mut self) { + InheritedComponents::apply_queued_shared_mutations(self); } /// Flushes queued entities and commands. @@ -3027,9 +2969,9 @@ impl World { /// Queued entities will be spawned, and then commands will be applied. #[inline] pub fn flush(&mut self) { + self.flush_shared_mutations(); self.flush_entities(); self.flush_components(); - self.flush_inherited_mut(); self.flush_commands(); } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 7f0b050e7f649..b0613325f1a2f 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -8,7 +8,7 @@ use crate::{ component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, entity::{ContainsEntity, Entities, Entity, EntityDoesNotExistError, EntityLocation}, inheritance::{ - InheritedArchetypeComponent, InheritedComponents, MutInherited, SharedMutComponentData, + InheritedArchetypeComponent, InheritedComponents, MutComponent, MutComponentSharedData, }, observer::Observers, prelude::Component, @@ -20,7 +20,14 @@ use crate::{ }; use bevy_platform::sync::atomic::Ordering; use bevy_ptr::{Ptr, UnsafeCellDeref}; -use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, panic::Location, ptr}; +use core::{ + any::TypeId, + cell::UnsafeCell, + fmt::Debug, + marker::PhantomData, + panic::Location, + ptr::{self, NonNull}, +}; use thiserror::Error; /// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid @@ -906,7 +913,7 @@ impl<'w> UnsafeEntityCell<'w> { /// - the [`UnsafeEntityCell`] has permission to access the component mutably /// - no other references to the component exist at the same time #[inline] - pub unsafe fn get_mut>(self) -> Option> { + pub unsafe fn get_mut>(self) -> Option> { // SAFETY: // - trait bound `T: Component` ensures component is mutable // - same safety requirements @@ -919,7 +926,7 @@ impl<'w> UnsafeEntityCell<'w> { /// - no other references to the component exist at the same time /// - the component `T` is mutable #[inline] - pub unsafe fn get_mut_assume_mutable(self) -> Option> { + pub unsafe fn get_mut_assume_mutable(self) -> Option> { // SAFETY: same safety requirements unsafe { self.get_mut_using_ticks_assume_mutable( @@ -939,7 +946,7 @@ impl<'w> UnsafeEntityCell<'w> { &self, last_change_tick: Tick, change_tick: Tick, - ) -> Option> { + ) -> Option> { self.world.assert_allows_mutable_access(); let component_id = self.world.components().get_id(TypeId::of::())?; @@ -958,15 +965,15 @@ impl<'w> UnsafeEntityCell<'w> { true, ) .map( - |(value, cells, caller, is_inherited, shared_data)| MutInherited { - value: value.as_ptr().cast(), + |(value, cells, caller, is_shared, shared_data)| MutComponent { + value: NonNull::new_unchecked(value.as_ptr().cast()), + added: NonNull::new_unchecked(cells.added.get()), + changed: NonNull::new_unchecked(cells.changed.get()), + changed_by: caller.map(|caller| NonNull::new_unchecked(caller.get())), this_run: change_tick, last_run: last_change_tick, - added: cells.added.get(), - changed: cells.changed.get(), - changed_by: caller.map(|caller| caller.deref_mut()), - is_inherited, - table_row: self.location.table_row.as_usize(), + is_shared, + table_row: self.location.table_row, shared_data, }, ) @@ -1270,7 +1277,7 @@ unsafe fn get_component_and_ticks( TickCells<'_>, MaybeLocation<&UnsafeCell<&'static Location<'static>>>, bool, - Option<&SharedMutComponentData>, + Option<&MutComponentSharedData>, )> { match storage_type { StorageType::Table => { @@ -1320,8 +1327,8 @@ unsafe fn get_component_and_ticks( let table = world.fetch_table(location)?; table .get_component(component_id, location.table_row) - .map(|ptr| { - ( + .and_then(|ptr| { + Some(( ptr, TickCells { added: table @@ -1336,54 +1343,34 @@ unsafe fn get_component_and_ticks( .map(|changed_by| changed_by.debug_checked_unwrap()), true, Some( - &*world + world .inherited_components() - .shared_table_components - .get() - .as_mut() - .debug_checked_unwrap() - .entry((component_id, original_table_id)) - .or_insert_with(|| SharedMutComponentData { - component_ptrs: Default::default(), - bump: Default::default(), - component_info: world - .components() - .get_info(component_id) - .unwrap() - .clone(), - }), + .get_shared_table_component_data( + world.components().get_info(component_id)?, + original_table_id, + ), ), - ) + )) }) } InheritedArchetypeComponent::Sparse { entity, .. } => world .fetch_sparse_set(component_id)? .get_with_ticks(*entity) - .map(|(a, b, c)| { - ( + .and_then(|(a, b, c)| { + Some(( a, b, c, true, Some( - &*world + world .inherited_components() - .shared_sparse_components - .get() - .as_mut() - .debug_checked_unwrap() - .entry((component_id, archetype.id())) - .or_insert_with(|| SharedMutComponentData { - component_ptrs: Default::default(), - bump: Default::default(), - component_info: world - .components() - .get_info(component_id) - .unwrap() - .clone(), - }), + .get_shared_sparse_component_data( + world.components().get_info(component_id)?, + location.archetype_id, + ), ), - ) + )) }), } }) diff --git a/crates/bevy_text/src/text_access.rs b/crates/bevy_text/src/text_access.rs index a98f063ef10cf..60b38b794d7de 100644 --- a/crates/bevy_text/src/text_access.rs +++ b/crates/bevy_text/src/text_access.rs @@ -1,7 +1,7 @@ use bevy_color::Color; use bevy_ecs::{ component::Mutable, - inheritance::MutInherited, + inheritance::MutComponent, prelude::*, system::{Query, SystemParam}, }; @@ -258,9 +258,9 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { ) -> Option<( Entity, usize, - MutInherited, - MutInherited, - MutInherited, + Mut, + MutComponent, + MutComponent, )> { // Root if index == 0 { @@ -328,7 +328,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// Gets the text value of a text span within a text block at a specific index in the flattened span list. - pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option> { + pub fn get_text(&mut self, root_entity: Entity, index: usize) -> Option> { self.get(root_entity, index).map(|(_, _, text, ..)| text) } @@ -337,7 +337,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { &mut self, root_entity: Entity, index: usize, - ) -> Option> { + ) -> Option> { self.get(root_entity, index).map(|(_, _, _, font, _)| font) } @@ -346,7 +346,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { &mut self, root_entity: Entity, index: usize, - ) -> Option> { + ) -> Option> { self.get(root_entity, index) .map(|(_, _, _, _, color)| color) } @@ -354,21 +354,21 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { /// Gets the text value of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn text(&mut self, root_entity: Entity, index: usize) -> MutInherited { + pub fn text(&mut self, root_entity: Entity, index: usize) -> Mut { self.get_text(root_entity, index).unwrap() } /// Gets the [`TextFont`] of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn font(&mut self, root_entity: Entity, index: usize) -> MutInherited { + pub fn font(&mut self, root_entity: Entity, index: usize) -> MutComponent { self.get_font(root_entity, index).unwrap() } /// Gets the [`TextColor`] of a text span within a text block at a specific index in the flattened span list. /// /// Panics if there is no span at the requested index. - pub fn color(&mut self, root_entity: Entity, index: usize) -> MutInherited { + pub fn color(&mut self, root_entity: Entity, index: usize) -> MutComponent { self.get_color(root_entity, index).unwrap() } @@ -379,9 +379,9 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { mut callback: impl FnMut( Entity, usize, - MutInherited, - MutInherited, - MutInherited, + Mut, + MutComponent, + MutComponent, ), ) { self.for_each_until(root_entity, |a, b, c, d, e| { @@ -391,11 +391,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// Invokes a callback on each span's string value in a text block, starting with the root entity. - pub fn for_each_text( - &mut self, - root_entity: Entity, - mut callback: impl FnMut(MutInherited), - ) { + pub fn for_each_text(&mut self, root_entity: Entity, mut callback: impl FnMut(Mut)) { self.for_each(root_entity, |_, _, text, _, _| { (callback)(text); }); @@ -405,7 +401,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { pub fn for_each_font( &mut self, root_entity: Entity, - mut callback: impl FnMut(MutInherited), + mut callback: impl FnMut(MutComponent), ) { self.for_each(root_entity, |_, _, _, font, _| { (callback)(font); @@ -416,7 +412,7 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { pub fn for_each_color( &mut self, root_entity: Entity, - mut callback: impl FnMut(MutInherited), + mut callback: impl FnMut(MutComponent), ) { self.for_each(root_entity, |_, _, _, _, color| { (callback)(color); @@ -433,9 +429,9 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { mut callback: impl FnMut( Entity, usize, - MutInherited, - MutInherited, - MutInherited, + Mut, + MutComponent, + MutComponent, ) -> bool, ) { // Root diff --git a/crates/bevy_transform/src/systems.rs b/crates/bevy_transform/src/systems.rs index ad53e137be859..9b70b1db79a3a 100644 --- a/crates/bevy_transform/src/systems.rs +++ b/crates/bevy_transform/src/systems.rs @@ -252,7 +252,7 @@ mod parallel { // TODO: this implementation could be used in no_std if there are equivalents of these. use alloc::{sync::Arc, vec::Vec}; use bevy_ecs::{ - entity::UniqueEntityIter, inheritance::MutInherited, prelude::*, system::lifetimeless::Read, + entity::UniqueEntityIter, inheritance::MutComponent, prelude::*, system::lifetimeless::Read, }; use bevy_tasks::{ComputeTaskPool, TaskPool}; use bevy_utils::Parallel; @@ -423,7 +423,7 @@ mod parallel { #[expect(unsafe_code, reason = "Mutating disjoint entities in parallel")] unsafe fn propagate_descendants_unchecked( parent: Entity, - p_global_transform: MutInherited, + p_global_transform: MutComponent, p_children: &Children, nodes: &NodeQuery, outbox: &mut Vec, diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index f59803b94a9fc..0b2def341c027 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -9,7 +9,7 @@ use bevy_ecs::{ change_detection::DetectChanges, component::Component, entity::Entity, - inheritance::MutInherited, + inheritance::MutComponent, query::With, reflect::ReflectComponent, system::{Query, Res, ResMut}, @@ -201,9 +201,9 @@ fn create_text_measure<'a>( spans: impl Iterator, block: Ref, text_pipeline: &mut TextPipeline, - mut content_size: MutInherited, - mut text_flags: MutInherited, - mut computed: MutInherited, + mut content_size: MutComponent, + mut text_flags: MutComponent, + mut computed: MutComponent, font_system: &mut CosmicFontSystem, ) { match text_pipeline.create_text_measure( @@ -298,8 +298,8 @@ fn queue_text( inverse_scale_factor: f32, block: &TextLayout, node: Ref, - mut text_flags: MutInherited, - text_layout_info: MutInherited, + mut text_flags: MutComponent, + text_layout_info: MutComponent, computed: &mut ComputedTextBlock, text_reader: &mut TextUiReader, font_system: &mut CosmicFontSystem, diff --git a/crates/bevy_winit/src/state.rs b/crates/bevy_winit/src/state.rs index ac85c350c747b..a3dd42b6247fb 100644 --- a/crates/bevy_winit/src/state.rs +++ b/crates/bevy_winit/src/state.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ change_detection::{DetectChanges, NonSendMut, Res}, entity::Entity, event::{EventCursor, EventWriter}, - inheritance::MutInherited, + inheritance::MutComponent, prelude::*, system::SystemState, world::FromWorld, @@ -955,7 +955,7 @@ pub fn winit_runner(mut app: App, event_loop: EventLoop) -> AppExit pub(crate) fn react_to_resize( window_entity: Entity, - window: &mut bevy_ecs::inheritance::MutInherited<'_, Window>, + window: &mut MutComponent<'_, Window>, size: PhysicalSize, window_resized: &mut EventWriter, ) { @@ -972,7 +972,7 @@ pub(crate) fn react_to_resize( pub(crate) fn react_to_scale_factor_change( window_entity: Entity, - window: &mut MutInherited<'_, Window>, + window: &mut MutComponent<'_, Window>, scale_factor: f64, window_backend_scale_factor_changed: &mut EventWriter, window_scale_factor_changed: &mut EventWriter, From 9a5dafe11516e20f25f6005cbc8d85864908746a Mon Sep 17 00:00:00 2001 From: eugineerd Date: Sun, 27 Apr 2025 23:24:19 +0000 Subject: [PATCH 15/16] fix abnormally slow foreach performance --- crates/bevy_ecs/src/query/iter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index abd9b19b00449..8a50521e3f12e 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -130,7 +130,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// /// # Safety /// - `range` must be in `[0, storage::entity_count)` or None. - #[inline] + #[inline(always)] pub(super) unsafe fn fold_over_storage_range( &mut self, mut accum: B, @@ -194,7 +194,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// - `table` must match D and F /// - `table_id` must match `table` /// - The query iteration must be dense (i.e. `self.query_state.is_dense` must be true). - #[inline] + #[inline(always)] pub(super) unsafe fn fold_over_table_range( &mut self, mut accum: B, @@ -256,7 +256,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// - all `indices` must be in `[0, archetype.len())`. /// - `archetype` must match D and F /// - The query iteration must not be dense (i.e. `self.query_state.is_dense` must be false). - #[inline] + #[inline(always)] pub(super) unsafe fn fold_over_archetype_range( &mut self, mut accum: B, @@ -325,7 +325,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// - `archetype` must match D and F /// - `archetype` must have the same length with it's table. /// - The query iteration must not be dense (i.e. `self.query_state.is_dense` must be false). - #[inline] + #[inline(always)] pub(super) unsafe fn fold_over_dense_archetype_range( &mut self, mut accum: B, @@ -906,7 +906,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Iterator for QueryIter<'w, 's, D, F> (min_size, Some(max_size)) } - #[inline] + #[inline(always)] fn fold(mut self, init: B, mut func: Func) -> B where Func: FnMut(B, Self::Item) -> B, From 65627e0cd6b34328c589ad45aa9b5abb3258cce5 Mon Sep 17 00:00:00 2001 From: eugineerd Date: Tue, 29 Apr 2025 16:15:47 +0000 Subject: [PATCH 16/16] fixed bug, more regressions :( --- crates/bevy_ecs/src/component.rs | 3 + crates/bevy_ecs/src/inheritance.rs | 32 ++-- crates/bevy_ecs/src/query/fetch.rs | 222 ++++++++++++----------- crates/bevy_ecs/src/query/filter.rs | 2 +- crates/bevy_ecs/src/query/world_query.rs | 63 ++----- crates/bevy_render/src/sync_world.rs | 2 +- 6 files changed, 157 insertions(+), 167 deletions(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 8a4e620277ea1..616d754e8628e 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -5,6 +5,7 @@ use crate::{ bundle::BundleInfo, change_detection::{MaybeLocation, MAX_CHANGE_AGE}, entity::{ComponentCloneCtx, Entity, EntityMapper, SourceComponent}, + inheritance::InheritedBehavior, query::DebugCheckedUnwrap, relationship::RelationshipHookMode, resource::Resource, @@ -486,6 +487,8 @@ pub trait Component: Send + Sync + 'static { /// A constant indicating the storage type used for this component. const STORAGE_TYPE: StorageType; + const INHERITED_BEHAVIOR: InheritedBehavior = InheritedBehavior::Shared; + /// A marker type to assist Bevy with determining if this component is /// mutable, or immutable. Mutable components will have [`Component`], /// while immutable components will instead have [`Component`]. diff --git a/crates/bevy_ecs/src/inheritance.rs b/crates/bevy_ecs/src/inheritance.rs index 4009fb6d94b30..e85a7e683efb5 100644 --- a/crates/bevy_ecs/src/inheritance.rs +++ b/crates/bevy_ecs/src/inheritance.rs @@ -198,10 +198,16 @@ impl Drop for MutComponentSharedData { } } +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum InheritedBehavior { + Disabled, + Shared, +} + impl MutComponentSharedData { + #[inline] fn alloc( bump: &Mutex, - bump_ptr: NonNull>, component_info: &ComponentInfo, ) -> NonNull { let bump_lock = bump.lock().unwrap(); @@ -222,16 +228,19 @@ impl MutComponentSharedData { unsafe { NonNull::new_unchecked(bumpalo::boxed::Box::into_raw(shared_data)) } } + #[inline] unsafe fn from_ptr<'a>( shared_data: NonNull, ) -> bumpalo::boxed::Box<'a, MutComponentSharedData> { unsafe { bumpalo::boxed::Box::from_raw(shared_data.as_ptr()) } } + #[inline] fn components(&self) -> &Mutex> { unsafe { self.component_ptrs.as_ref() } } + #[inline] fn get_or_cloned<'w, T: Component>( &self, data: &'w T, @@ -266,7 +275,7 @@ impl MutComponentSharedData { } } - #[inline(always)] + #[inline] fn try_get<'w, T: Component>(&self, table_row: TableRow) -> Option> { unsafe { self.component_ptrs @@ -331,7 +340,7 @@ impl<'w, T: Component> MutComponent<'w, T> { ) }; - if self.is_shared { + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && self.is_shared { #[cold] #[inline(never)] fn unlikely<'w, R, T: Component>( @@ -375,7 +384,7 @@ impl<'w, T: Component> MutComponent<'w, T> { &mut self, func: impl FnOnce(ComponentMut<'w, T>) -> R, ) -> R { - if self.is_shared { + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && self.is_shared { #[cold] #[inline(never)] #[track_caller] @@ -540,7 +549,8 @@ impl<'w, T: Component> Deref for MutComponent<'w, T> { } impl<'w, T: Component> AsMut for MutComponent<'w, T> { - #[inline] + #[track_caller] + #[inline(always)] fn as_mut(&mut self) -> &mut T { self.deref_mut() } @@ -990,11 +1000,7 @@ impl InheritedComponents { let ptr = lock .entry((component_info.id(), table_id)) .or_insert_with(|| { - MutComponentSharedData::alloc( - self.shared_components_bump(), - self.shared_components_bump, - component_info, - ) + MutComponentSharedData::alloc(self.shared_components_bump(), component_info) }); unsafe { ptr.as_ref() } } @@ -1010,11 +1016,7 @@ impl InheritedComponents { let ptr = lock .entry((component_info.id(), archetype_id)) .or_insert_with(|| { - MutComponentSharedData::alloc( - self.shared_components_bump(), - self.shared_components_bump, - component_info, - ) + MutComponentSharedData::alloc(self.shared_components_bump(), component_info) }); unsafe { ptr.as_ref() } } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 17cb0fa43e767..da2d76a423989 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -5,8 +5,8 @@ use crate::{ component::{Component, ComponentId, ComponentInfo, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, inheritance::{ - InheritedArchetypeComponent, InheritedComponents, MutComponent, MutComponentPtrs, - MutComponentSharedData, + InheritedArchetypeComponent, InheritedBehavior, InheritedComponents, MutComponent, + MutComponentPtrs, MutComponentSharedData, }, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, @@ -22,7 +22,7 @@ use core::{ }; use smallvec::SmallVec; use std::boxed::Box; -use variadics_please::all_tuples; +use variadics_please::{all_tuples, all_tuples_enumerated}; /// Types that can be fetched from a [`World`] using a [`Query`]. /// @@ -316,11 +316,11 @@ pub unsafe trait QueryData: WorldQuery { table_row: TableRow, ) -> Self::Item<'w>; - unsafe fn fetch_inherited<'w>( + unsafe fn fetch_shared<'w>( fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - _is_inherited: bool, + _is_shared: bool, ) -> Self::Item<'w> { Self::fetch(fetch, entity, table_row) } @@ -1229,7 +1229,10 @@ unsafe impl WorldQuery for &T { unsafe { Self::set_table(fetch, component_id, table, table_id); } - } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && archetype.has_inherited_components() + && !archetype.contains(*component_id) + { match archetype .inherited_components .get(component_id) @@ -1248,9 +1251,12 @@ unsafe impl WorldQuery for &T { fetch: &mut ReadFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, - table_id: TableId, + _table_id: TableId, ) { - if table.has_inherited_components() && !table.has_column(component_id) { + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && !table.has_column(component_id) + { let info = table .inherited_components .get(&component_id) @@ -1319,12 +1325,13 @@ unsafe impl WorldQuery for &T { } #[inline(always)] - fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { - if Self::IS_DENSE { - fetch.is_shared_table_component - } else { - fetch.shared_sparse_component_entity.is_some() - } + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && if Self::IS_DENSE { + fetch.is_shared_table_component + } else { + fetch.shared_sparse_component_entity.is_some() + } } } @@ -1344,24 +1351,19 @@ unsafe impl QueryData for &T { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - Self::fetch_inherited( - fetch, - entity, - table_row, - Self::has_inherited_components(fetch), - ) + Self::fetch_shared(fetch, entity, table_row, Self::is_shared(fetch)) } #[inline(always)] - unsafe fn fetch_inherited<'w>( + unsafe fn fetch_shared<'w>( fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - is_inherited: bool, + is_shared: bool, ) -> Self::Item<'w> { fetch.components.extract( |table| { - let idx = if is_inherited { + let idx = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { 0 } else { table_row.as_usize() @@ -1374,7 +1376,7 @@ unsafe impl QueryData for &T { item.deref() }, |sparse_set| { - let entity = if is_inherited { + let entity = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { fetch.shared_sparse_component_entity.debug_checked_unwrap() } else { entity @@ -1485,7 +1487,10 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { unsafe { Self::set_table(fetch, component_id, table, archetype.table_id()); } - } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && archetype.has_inherited_components() + && !archetype.contains(*component_id) + { match archetype .inherited_components .get(component_id) @@ -1504,9 +1509,12 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { fetch: &mut RefFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, - table_id: TableId, + _table_id: TableId, ) { - if table.has_inherited_components() && !table.has_column(component_id) { + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && !table.has_column(component_id) + { let info = table .inherited_components .get(&component_id) @@ -1606,12 +1614,13 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { } #[inline] - fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { - if Self::IS_DENSE { - fetch.is_shared_table_component - } else { - fetch.shared_sparse_component_entity.is_some() - } + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && if Self::IS_DENSE { + fetch.is_shared_table_component + } else { + fetch.shared_sparse_component_entity.is_some() + } } } @@ -1631,28 +1640,19 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - Self::fetch_inherited( - fetch, - entity, - table_row, - Self::has_inherited_components(fetch), - ) + Self::fetch_shared(fetch, entity, table_row, Self::is_shared(fetch)) } #[inline(always)] - unsafe fn fetch_inherited<'w>( + unsafe fn fetch_shared<'w>( fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - is_inherited: bool, + is_shared: bool, ) -> Self::Item<'w> { fetch.components.extract( |table| { - let idx = if is_inherited { - 0 - } else { - table_row.as_usize() - }; + let idx = if is_shared { 0 } else { table_row.as_usize() }; // SAFETY: set_table was previously called let (table_components, added_ticks, changed_ticks, callers) = @@ -1679,7 +1679,7 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { } }, |sparse_set| { - let entity = if is_inherited { + let entity = if is_shared { fetch.shared_sparse_component_entity.debug_checked_unwrap() } else { entity @@ -1800,7 +1800,10 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { unsafe { Self::set_table(fetch, component_id, table, archetype.table_id()); } - } else if archetype.has_inherited_components() && !archetype.contains(*component_id) { + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && archetype.has_inherited_components() + && !archetype.contains(*component_id) + { match archetype .inherited_components .get(component_id) @@ -1826,7 +1829,10 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { table: &'w Table, table_id: TableId, ) { - if table.has_inherited_components() && !table.has_column(component_id) { + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && !table.has_column(component_id) + { // set_shared_components(fetch, component_id, table, table_id); // #[cold] @@ -1942,8 +1948,8 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { } #[inline(always)] - fn has_inherited_components<'w>(fetch: &Self::Fetch<'w>) -> bool { - fetch.shared_component_data.is_some() + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && fetch.shared_component_data.is_some() } } @@ -1963,24 +1969,19 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - Self::fetch_inherited( - fetch, - entity, - table_row, - Self::has_inherited_components(fetch), - ) + Self::fetch_shared(fetch, entity, table_row, Self::is_shared(fetch)) } #[inline(always)] - unsafe fn fetch_inherited<'w>( + unsafe fn fetch_shared<'w>( fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - is_inherited: bool, + is_shared: bool, ) -> Self::Item<'w> { fetch.components.extract( |table| { - let idx = if is_inherited { + let idx = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { 0 } else { table_row.as_usize() @@ -2005,13 +2006,13 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T changed_by: caller.map(|caller| NonNull::new_unchecked(caller.get())), this_run: fetch.this_run, last_run: fetch.last_run, - is_shared: is_inherited, + is_shared, shared_data: fetch.shared_component_data, table_row, } }, |sparse_set| { - let entity = if is_inherited { + let entity = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { fetch.shared_sparse_component_entity.debug_checked_unwrap() } else { entity @@ -2032,7 +2033,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T changed_by: caller.map(|caller| NonNull::new_unchecked(caller.get())), last_run: fetch.last_run, this_run: fetch.this_run, - is_shared: is_inherited, + is_shared, shared_data: fetch.shared_component_data, table_row, } @@ -2126,6 +2127,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { ) -> bool { <&mut T as WorldQuery>::matches_component_set(state, set_contains_id) } + + #[inline(always)] + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + <&mut T as WorldQuery>::is_shared(fetch) + } } // SAFETY: access of `Ref` is a subset of `Mut` @@ -2148,7 +2154,17 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> entity: Entity, table_row: TableRow, ) -> Self::Item<'w> { - <&mut T as QueryData>::fetch(fetch, entity, table_row) + Self::fetch_shared(fetch, entity, table_row, Self::is_shared(fetch)) + } + + #[inline(always)] + unsafe fn fetch_shared<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + is_shared: bool, + ) -> Self::Item<'w> { + <&mut T as QueryData>::fetch_shared(fetch, entity, table_row, is_shared) } } @@ -2259,6 +2275,10 @@ unsafe impl WorldQuery for Option { ) -> bool { true } + + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + T::is_shared(&fetch.fetch) + } } // SAFETY: defers to soundness of `T: WorldQuery` impl @@ -2276,11 +2296,21 @@ unsafe impl QueryData for Option { fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, + ) -> Self::Item<'w> { + Self::fetch_shared(fetch, entity, table_row, Self::is_shared(fetch)) + } + + #[inline(always)] + unsafe fn fetch_shared<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + is_shared: bool, ) -> Self::Item<'w> { fetch .matches // SAFETY: The invariants are upheld by the caller. - .then(|| unsafe { T::fetch(&mut fetch.fetch, entity, table_row) }) + .then(|| unsafe { T::fetch_shared(&mut fetch.fetch, entity, table_row, is_shared) }) } } @@ -2393,7 +2423,10 @@ unsafe impl WorldQuery for Has { archetype: &'w Archetype, _table: &Table, ) { - *fetch = archetype.contains(*state); + *fetch = archetype.contains(*state) + || (T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && archetype.has_inherited_components() + && archetype.inherited_components.contains_key(state)); } #[inline] @@ -2403,7 +2436,10 @@ unsafe impl WorldQuery for Has { table: &'w Table, _table_id: TableId, ) { - *fetch = table.has_column(*state); + *fetch = table.has_column(*state) + || (T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && table.inherited_components.contains_key(state)); } fn update_component_access( @@ -2461,7 +2497,7 @@ unsafe impl ReadOnlyQueryData for Has {} pub struct AnyOf(PhantomData); macro_rules! impl_tuple_query_data { - ($(#[$meta:meta])* $(($name: ident, $state: ident, $mask: ident)),*) => { + ($(#[$meta:meta])* $(($n: tt, $name: ident, $state: ident)),*) => { #[expect( clippy::allow_attributes, reason = "This is a tuple-related macro; as such the lints below may not always apply." @@ -2498,9 +2534,9 @@ macro_rules! impl_tuple_query_data { entity: Entity, table_row: TableRow ) -> Self::Item<'w> { - let ($($name,)*) = &mut fetch.inner; + let ($($name,)*) = &mut fetch.0; // SAFETY: The invariants are upheld by the caller. - ($(unsafe { $name::fetch_inherited($name, entity, table_row, fetch.inherited_filter & $mask != 0 ) },)*) + ($(unsafe { $name::fetch_shared($name, entity, table_row, fetch.1 & (1 << $n) != 0 ) },)*) } } @@ -2512,7 +2548,7 @@ macro_rules! impl_tuple_query_data { } macro_rules! impl_anytuple_fetch { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($n:tt, $name: ident, $state: ident)),*) => { $(#[$meta])* #[expect( clippy::allow_attributes, @@ -2536,21 +2572,21 @@ macro_rules! impl_anytuple_fetch { /// `update_component_access` replaces the filters with a disjunction where every element is a conjunction of the previous filters and the filters of one of the subqueries. /// This is sound because `matches_component_set` returns a disjunction of the results of the subqueries' implementations. unsafe impl<$($name: WorldQuery),*> WorldQuery for AnyOf<($($name,)*)> { - type Fetch<'w> = ($(($name::Fetch<'w>, bool),)*); + type Fetch<'w> = (($(($name::Fetch<'w>, bool),)*), usize); type State = ($($name::State,)*); fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { - let ($($name,)*) = fetch; - ($( + let ($($name,)*) = fetch.0; + (($( ($name::shrink_fetch($name.0), $name.1), - )*) + )*), fetch.1) } #[inline] unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> { let ($($name,)*) = state; // SAFETY: The invariants are upheld by the caller. - ($(( unsafe { $name::init_fetch(_world, $name, _last_run, _this_run) }, false),)*) + (($(( unsafe { $name::init_fetch(_world, $name, _last_run, _this_run) }, false),)*), 0) } const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; @@ -2562,26 +2598,28 @@ macro_rules! impl_anytuple_fetch { _archetype: &'w Archetype, _table: &'w Table ) { - let ($($name,)*) = _fetch; + let ($($name,)*) = &mut _fetch.0; let ($($state,)*) = _state; $( $name.1 = $name::matches_component_set($state, &|id| _archetype.contains(id)); if $name.1 { // SAFETY: The invariants are upheld by the caller. unsafe { $name::set_archetype(&mut $name.0, $state, _archetype, _table); } + _fetch.1 |= ($name::is_shared(&$name.0) as usize & 1) << $n; } )* } #[inline] unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table, _table_id: TableId) { - let ($($name,)*) = _fetch; + let ($($name,)*) = &mut _fetch.0; let ($($state,)*) = _state; $( $name.1 = $name::matches_component_set($state, &|id| _table.has_column(id)); if $name.1 { // SAFETY: The invariants are required to be upheld by the caller. unsafe { $name::set_table(&mut $name.0, $state, _table, _table_id); } + _fetch.1 |= ($name::is_shared(&$name.0) as usize & 1) << $n; } )* } @@ -2661,10 +2699,10 @@ macro_rules! impl_anytuple_fetch { _entity: Entity, _table_row: TableRow ) -> Self::Item<'w> { - let ($($name,)*) = _fetch; + let ($($name,)*) = &mut _fetch.0; ($( // SAFETY: The invariants are required to be upheld by the caller. - $name.1.then(|| unsafe { $name::fetch(&mut $name.0, _entity, _table_row) }), + $name.1.then(|| unsafe { $name::fetch_shared(&mut $name.0, _entity, _table_row, _fetch.1 & (1 << $n) != 0) }), )*) } } @@ -2675,33 +2713,15 @@ macro_rules! impl_anytuple_fetch { }; } -const NUMBER: usize = 1 << 0; -const NUMBER0: usize = 1 << 0; -const NUMBER1: usize = 1 << 1; -const NUMBER2: usize = 1 << 2; -const NUMBER3: usize = 1 << 3; -const NUMBER4: usize = 1 << 4; -const NUMBER5: usize = 1 << 5; -const NUMBER6: usize = 1 << 6; -const NUMBER7: usize = 1 << 7; -const NUMBER8: usize = 1 << 8; -const NUMBER9: usize = 1 << 9; -const NUMBER10: usize = 1 << 10; -const NUMBER11: usize = 1 << 11; -const NUMBER12: usize = 1 << 12; -const NUMBER13: usize = 1 << 13; -const NUMBER14: usize = 1 << 14; - -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_tuple_query_data, 0, 15, F, - S, - NUMBER + S ); -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_anytuple_fetch, 0, diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 34b5a918ec1f9..3a6ef245d7fe3 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -532,7 +532,7 @@ macro_rules! impl_tuple_query_filter { entity: Entity, table_row: TableRow ) -> bool { - let ($($name,)*) = &mut fetch.inner; + let ($($name,)*) = &mut fetch.0; // SAFETY: The invariants are upheld by the caller. true $(&& unsafe { $name::filter_fetch($name, entity, table_row) })* } diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index f481b8269de6d..9753a143333cd 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -7,7 +7,7 @@ use crate::{ storage::{Table, TableId}, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -use variadics_please::all_tuples; +use variadics_please::all_tuples_enumerated; /// Types that can be used as parameters in a [`Query`]. /// Types that implement this should also implement either [`QueryData`] or [`QueryFilter`] @@ -139,23 +139,14 @@ pub unsafe trait WorldQuery { set_contains_id: &impl Fn(ComponentId) -> bool, ) -> bool; - fn has_inherited_components<'w>(_fetch: &Self::Fetch<'w>) -> bool { + #[inline(always)] + fn is_shared<'w>(_fetch: &Self::Fetch<'w>) -> bool { false } } -#[derive(Clone, Copy)] -pub struct TupleFetch<'w, S> -where - S: Clone, -{ - pub inherited_filter: usize, - pub inner: S, - pub phantom: PhantomData<&'w ()>, -} - macro_rules! impl_tuple_world_query { - ($(#[$meta:meta])* $(($name: ident, $state: ident, $mask: ident)),*) => { + ($(#[$meta:meta])* $(($n:tt, $name: ident, $state: ident)),*) => { #[expect( clippy::allow_attributes, @@ -180,28 +171,21 @@ macro_rules! impl_tuple_world_query { /// `update_component_access` adds all `With` and `Without` filters from the subqueries. /// This is sound because `matches_component_set` always returns `false` if any the subqueries' implementations return `false`. unsafe impl<$($name: WorldQuery),*> WorldQuery for ($($name,)*) { - type Fetch<'w> = TupleFetch<'w, ($($name::Fetch<'w>,)*)>; + type Fetch<'w> = (($($name::Fetch<'w>,)*), usize); type State = ($($name::State,)*); #[inline(always)] fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { - let ($($name,)*) = fetch.inner; - TupleFetch { - inner: ($( $name::shrink_fetch($name),)*), - ..fetch - } + let ($($name,)*) = fetch.0; + (($( $name::shrink_fetch($name),)*), fetch.1) } #[inline(always)] unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { let ($($name,)*) = state; // SAFETY: The invariants are upheld by the caller. - TupleFetch { - inherited_filter: 0, - inner: ($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*), - phantom: Default::default(), - } + (($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*), 0) } const IS_DENSE: bool = true $(&& $name::IS_DENSE)*; @@ -213,23 +197,22 @@ macro_rules! impl_tuple_world_query { archetype: &'w Archetype, table: &'w Table ) { - let ($($name,)*) = &mut fetch.inner; + let ($($name,)*) = &mut fetch.0; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. $(unsafe { $name::set_archetype($name, $state, archetype, table); })* - $(if $name::has_inherited_components($name) {fetch.inherited_filter &= $mask} )* + $(if $name::is_shared($name) {fetch.1 |= 1 << $n;} )* } #[inline(always)] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table, table_id: TableId) { - let ($($name,)*) = &mut fetch.inner; + let ($($name,)*) = &mut fetch.0; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. $(unsafe { $name::set_table($name, $state, table, table_id); })* - $(if $name::has_inherited_components($name) {fetch.inherited_filter &= $mask} )* + $(if $name::is_shared($name) {fetch.1 |= 1 << $n;} )* } - fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { let ($($name,)*) = state; $($name::update_component_access($name, access);)* @@ -249,29 +232,11 @@ macro_rules! impl_tuple_world_query { }; } -const NUMBER: usize = 1 << 0; -const NUMBER0: usize = 1 << 0; -const NUMBER1: usize = 1 << 1; -const NUMBER2: usize = 1 << 2; -const NUMBER3: usize = 1 << 3; -const NUMBER4: usize = 1 << 4; -const NUMBER5: usize = 1 << 5; -const NUMBER6: usize = 1 << 6; -const NUMBER7: usize = 1 << 7; -const NUMBER8: usize = 1 << 8; -const NUMBER9: usize = 1 << 9; -const NUMBER10: usize = 1 << 10; -const NUMBER11: usize = 1 << 11; -const NUMBER12: usize = 1 << 12; -const NUMBER13: usize = 1 << 13; -const NUMBER14: usize = 1 << 14; - -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_tuple_world_query, 0, 15, F, - S, - NUMBER + S ); diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index a3db9ed265b05..ee780a42eaedf 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -342,7 +342,7 @@ mod render_entities_world_query_impls { ) { // SAFETY: defers to the `&T` implementation, with T set to `RenderEntity`. unsafe { - <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table, table_id) + <&RenderEntity as WorldQuery>::set_table(fetch, &component_id, table, table_id); } }