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_asset/src/asset_changed.rs b/crates/bevy_asset/src/asset_changed.rs index 40723d7b08d45..6d1f60c3873c9 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/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/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/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/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 2468c5e9a8fbf..0d0f3c80bf6f6 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -23,8 +23,10 @@ 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}, }; use alloc::{boxed::Box, vec::Vec}; use bevy_platform::collections::HashMap; @@ -360,6 +362,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); } } @@ -376,6 +380,7 @@ pub struct Archetype { entities: Vec, components: ImmutableSparseSet, pub(crate) flags: ArchetypeFlags, + pub(crate) inherited_components: HashMap, } impl Archetype { @@ -408,10 +413,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 +437,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, @@ -438,6 +455,7 @@ impl Archetype { components: archetype_components.into_immutable(), edges: Default::default(), flags, + inherited_components: Default::default(), } } @@ -517,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] @@ -622,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. @@ -719,6 +752,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 +860,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/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 5666d90c53a42..ef66d58781bd0 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, + archetypes, + &mut storages.tables, + 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, + archetypes, + &mut storages.tables, + 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 { @@ -1289,7 +1308,38 @@ impl<'w> BundleInserter<'w> { } }; - let new_archetype = &*new_archetype; + let (new_archetype_id, is_inherited) = (new_archetype.id(), new_archetype.is_inherited()); + + if is_inherited { + 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, + ); + } + } + + 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() }; @@ -1399,6 +1449,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/component.rs b/crates/bevy_ecs/src/component.rs index bfa55804b616a..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`]. @@ -2641,7 +2644,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/entity/entity_set.rs b/crates/bevy_ecs/src/entity/entity_set.rs index e4860685fe07f..a5bf4d079c1ab 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::MutComponent; 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 new file mode 100644 index 0000000000000..e85a7e683efb5 --- /dev/null +++ b/crates/bevy_ecs/src/inheritance.rs @@ -0,0 +1,1356 @@ +//! 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::boxed::Box; +use alloc::collections::vec_deque::VecDeque; +use alloc::string::ToString; +use alloc::vec::Vec; +use bevy_platform::{ + collections::{HashMap, HashSet}, + sync::Mutex, +}; +use bumpalo::Bump; +use core::{ + alloc::Layout, + ops::{Deref, DerefMut}, + panic::Location, + ptr::NonNull, +}; + +use bevy_ptr::OwningPtr; + +use crate::{ + archetype::{ArchetypeComponentId, ArchetypeEntity, ArchetypeId, ArchetypeRecord, Archetypes}, + change_detection::MaybeLocation, + component::{ + Component, ComponentCloneBehavior, ComponentDescriptor, ComponentId, ComponentInfo, + HookContext, StorageType, Tick, + }, + entity::{Entities, Entity, EntityHashMap, EntityLocation}, + prelude::{DetectChanges, DetectChangesMut}, + storage::{TableId, TableRow, Tables}, + world::{DeferredWorld, Mut, World}, +}; +use crate::{change_detection::TicksMut, query::DebugCheckedUnwrap}; + +#[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(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(Clone)] +pub(crate) struct MutComponentSharedData { + pub(crate) component_info: ComponentInfo, + pub(crate) bump: NonNull>, + pub(crate) component_ptrs: NonNull>>, +} + +impl Drop for MutComponentSharedData { + fn drop(&mut self) { + unsafe { drop(bumpalo::boxed::Box::from_raw(self.component_ptrs.as_ptr())) } + } +} + +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum InheritedBehavior { + Disabled, + Shared, +} + +impl MutComponentSharedData { + #[inline] + fn alloc( + bump: &Mutex, + 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)) } + } + + #[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, + 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, 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] + 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 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) 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 T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && 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 T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && 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: 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: Component> AsRef for MutComponent<'w, T> { + #[inline] + fn as_ref(&self) -> &T { + self.deref() + } +} + +impl<'w, T: Component> Deref for MutComponent<'w, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.dispatch(|args| args.value) + } +} + +impl<'w, T: Component> AsMut for MutComponent<'w, T> { + #[track_caller] + #[inline(always)] + fn as_mut(&mut self) -> &mut T { + self.deref_mut() + } +} + +impl<'w, T: Component> DerefMut for MutComponent<'w, T> { + #[track_caller] + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + self.dispatch_mut::(|args| args.value) + } +} + +impl<'w, T: Component> DetectChanges for MutComponent<'w, T> { + #[inline(always)] + fn is_added(&self) -> bool { + self.dispatch(|args| args.added.is_newer_than(self.last_run, self.this_run)) + } + + #[inline(always)] + fn is_changed(&self) -> bool { + self.dispatch(|args| args.changed.is_newer_than(self.last_run, self.this_run)) + } + + #[inline(always)] + fn last_changed(&self) -> Tick { + self.dispatch(|args| *args.changed) + } + + #[inline(always)] + fn changed_by(&self) -> MaybeLocation { + self.dispatch(|args| args.changed_by) + } + + #[inline(always)] + fn added(&self) -> Tick { + self.dispatch(|args| *args.added) + } +} + +impl<'w, T: Component> DetectChangesMut for MutComponent<'w, T> { + type Inner = as DetectChangesMut>::Inner; + + #[inline(always)] + #[track_caller] + fn set_changed(&mut self) { + self.dispatch_mut::(|_| {}); + } + + #[inline(always)] + #[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.dispatch_mut::(|args| args.value) + } + + #[inline(always)] + #[track_caller] + fn set_added(&mut self) { + let this_run = self.this_run; + self.dispatch_mut::(|args| *args.added = this_run); + } + + #[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() + } +} + +/// 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, + + pub(crate) queued_shared_mutations_level: usize, + pub(crate) shared_table_components: + Mutex>>, + pub(crate) shared_sparse_components: + 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, + entities: &Entities, + archetypes: &mut Archetypes, + tables: &mut Tables, + archetype_id: ArchetypeId, + ) { + let archetype = &archetypes.archetypes[archetype_id.index()]; + if !archetype.has_inherited_components() { + return; + } + let archetype_inherited_components: HashMap = + archetype + .table_components() + .filter_map(|id| { + self.ids_to_entities.get(&id).and_then(|entity| { + entities.get(*entity).and_then(|location| { + archetypes + .get(location.archetype_id) + .map(|archetype| (archetype, *entity, location)) + }) + }) + }) + .flat_map(|(inherited_archetype, entity, location)| { + inherited_archetype + .components_with_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( + archetypes + .get(inherited_archetype.id()) + .unwrap() + .inherited_components + .iter() + .map(|(a, b)| (*a, *b)), + ) + }) + .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() { + 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. + 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; + } + + let archetype = &mut archetypes[archetype_id]; + archetype.inherited_components = archetype_inherited_components; + } + + /// 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, + 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 new_archetype = &archetypes[new_archetype_id]; + let new_components: HashMap<_, _> = new_archetype + .components_with_archetype_component_id() + .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_archetype.contains(component_id)) + .collect(); + + 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, 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 mut archetype_inherited_components = + core::mem::take(&mut archetypes[archetype_id].inherited_components); + archetype_inherited_components.retain(|component_id, _| { + !new_components.contains_key(component_id) + && !removed_components.contains(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 &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 archetype table's inherited components + // This needs to be done only if entity moved tables + if UPDATE_TABLES { + 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, _| { + !new_components.contains_key(component_id) + && !removed_components.contains(component_id) + }); + 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)) + }) { + base_entities.push_back((entity, component_id)); + } + processed_archetypes.insert(archetype.id()); + } + } + } + } + + 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(), 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(), 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)] +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 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] + 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 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, 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::(), 11); + } +} diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 99f95763d572a..86ca40b69df73 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; diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index cd632f7b14f22..da2d76a423989 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -2,19 +2,27 @@ 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, InheritedBehavior, InheritedComponents, MutComponent, + MutComponentPtrs, MutComponentSharedData, + }, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, - storage::{ComponentSparseSet, Table, TableRow}, + storage::{ComponentSparseSet, Table, TableId, TableRow, Tables}, world::{ unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, }, }; 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 variadics_please::all_tuples; +use std::boxed::Box; +use variadics_please::{all_tuples, all_tuples_enumerated}; /// Types that can be fetched from a [`World`] using a [`Query`]. /// @@ -307,6 +315,15 @@ pub unsafe trait QueryData: WorldQuery { entity: Entity, table_row: TableRow, ) -> Self::Item<'w>; + + unsafe fn fetch_shared<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: TableRow, + _is_shared: bool, + ) -> Self::Item<'w> { + Self::fetch(fetch, entity, table_row) + } } /// A [`QueryData`] that is read only. @@ -350,7 +367,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 +449,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 +530,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 +616,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 +702,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 +803,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 +900,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 +1000,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 +1098,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) {} @@ -1106,6 +1158,9 @@ pub struct ReadFetch<'w, T: Component> { // T::STORAGE_TYPE = StorageType::SparseSet Option<&'w ComponentSparseSet>, >, + inherited_tables: &'w Tables, + is_shared_table_component: bool, + shared_sparse_component_entity: Option, } impl Clone for ReadFetch<'_, T> { @@ -1128,7 +1183,7 @@ unsafe impl WorldQuery for &T { fetch } - #[inline] + #[inline(always)] unsafe fn init_fetch<'w>( world: UnsafeWorldCell<'w>, &component_id: &ComponentId, @@ -1146,6 +1201,11 @@ unsafe impl WorldQuery for &T { unsafe { world.storages().sparse_sets.get(component_id) } }, ), + // 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, } } @@ -1156,35 +1216,85 @@ unsafe impl WorldQuery for &T { } }; - #[inline] + #[inline(always)] 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); + } + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && 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); + } + _ => unreachable!(), } } } - #[inline] + #[inline(always)] unsafe fn set_table<'w>( fetch: &mut ReadFetch<'w, T>, &component_id: &ComponentId, table: &'w Table, + _table_id: TableId, ) { - let table_data = Some( - table - .get_data_slice_for(component_id) - .debug_checked_unwrap() + if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && !table.has_column(component_id) + { + 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 - unsafe { fetch.components.set_table(table_data) }; + ); + // 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.is_shared_table_component = true; + }; + } 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( @@ -1213,6 +1323,16 @@ unsafe impl WorldQuery for &T { ) -> bool { set_contains_id(state) } + + #[inline(always)] + 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() + } + } } /// SAFETY: `Self` is the same as `Self::ReadOnly` @@ -1230,16 +1350,38 @@ unsafe impl QueryData for &T { 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.components.extract( |table| { + let idx = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { + 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()) }; + let item = unsafe { table.get(idx) }; item.deref() }, |sparse_set| { + let entity = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { + fetch.shared_sparse_component_entity.debug_checked_unwrap() + } else { + entity + }; + // SAFETY: Caller ensures `entity` is in range. let item = unsafe { sparse_set @@ -1273,6 +1415,9 @@ 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> { @@ -1315,6 +1460,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, 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 }, + is_shared_table_component: false, + shared_sparse_component_entity: None, } } @@ -1329,13 +1479,27 @@ 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()); + } + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && 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); + } + _ => unreachable!(), } } } @@ -1345,18 +1509,81 @@ 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(( - 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 T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && !table.has_column(component_id) + { + 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.is_shared_table_component = true; + }; + } 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( @@ -1385,6 +1612,16 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { ) -> bool { set_contains_id(state) } + + #[inline] + 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() + } + } } /// SAFETY: `Self` is the same as `Self::ReadOnly` @@ -1402,21 +1639,33 @@ 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_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.components.extract( |table| { + 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) = unsafe { table.debug_checked_unwrap() }; // 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(idx) }; // 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(idx) }; // 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(idx) }; // 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(idx) }); Ref { value: component.deref(), @@ -1430,6 +1679,12 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { } }, |sparse_set| { + let entity = if is_shared { + 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 @@ -1468,6 +1723,11 @@ pub struct WriteFetch<'w, T: Component> { >, last_run: Tick, this_run: Tick, + shared_component_data: Option<&'w MutComponentSharedData>, + shared_sparse_component_entity: Option, + inherited_tables: &'w Tables, + inherited_components: &'w InheritedComponents, + component_info: &'w ComponentInfo, } impl Clone for WriteFetch<'_, T> { @@ -1490,7 +1750,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, @@ -1510,6 +1770,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(), } } @@ -1520,38 +1788,136 @@ 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, - _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()); + } + } else if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && 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 + .get_shared_sparse_component_data(fetch.component_info, archetype.id()), + ); + } + _ => unreachable!(), } } } - #[inline] + #[inline(always)] unsafe fn set_table<'w>( 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(( - 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 T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && 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 + .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(( + 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( @@ -1580,15 +1946,20 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { ) -> bool { set_contains_id(state) } + + #[inline(always)] + fn is_shared<'w>(fetch: &Self::Fetch<'w>) -> bool { + T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && 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> = MutComponent<'w, T>; - fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> { + fn shrink<'wlong: 'wshort, 'wshort>(item: MutComponent<'wlong, T>) -> MutComponent<'wshort, T> { item } @@ -1597,34 +1968,56 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T 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.components.extract( |table| { + let idx = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { + 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(table_row.as_usize()) }; + let component = unsafe { table_components.get(idx) }; // 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(idx) }; // 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(idx) }; // 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()), + let caller = callers.map(|callers| unsafe { callers.get(idx) }); + + 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, + is_shared, + shared_data: fetch.shared_component_data, + table_row, } }, |sparse_set| { + let entity = if T::INHERITED_BEHAVIOR == InheritedBehavior::Shared && is_shared { + 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 @@ -1633,10 +2026,16 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T .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()), + 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, + is_shared, + shared_data: fetch.shared_component_data, + table_row, } }, ) @@ -1687,8 +2086,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` @@ -1723,16 +2127,21 @@ 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` 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> = MutComponent<'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) } @@ -1744,8 +2153,18 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow, - ) -> Mut<'w, T> { - <&mut T as QueryData>::fetch(fetch, entity, table_row) + ) -> 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> { + <&mut T as QueryData>::fetch_shared(fetch, entity, table_row, is_shared) } } @@ -1812,12 +2231,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); } } } @@ -1851,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 @@ -1868,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) }) } } @@ -1985,12 +2423,23 @@ 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] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { - *fetch = table.has_column(*state); + unsafe fn set_table<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + table: &'w Table, + _table_id: TableId, + ) { + *fetch = table.has_column(*state) + || (T::INHERITED_BEHAVIOR == InheritedBehavior::Shared + && table.has_inherited_components() + && table.inherited_components.contains_key(state)); } fn update_component_access( @@ -2048,7 +2497,7 @@ unsafe impl ReadOnlyQueryData for Has {} pub struct AnyOf(PhantomData); macro_rules! impl_tuple_query_data { - ($(#[$meta:meta])* $(($name: ident, $state: 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." @@ -2085,9 +2534,9 @@ macro_rules! impl_tuple_query_data { entity: Entity, table_row: TableRow ) -> Self::Item<'w> { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.0; // SAFETY: The invariants are upheld by the caller. - ($(unsafe { $name::fetch($name, entity, table_row) },)*) + ($(unsafe { $name::fetch_shared($name, entity, table_row, fetch.1 & (1 << $n) != 0 ) },)*) } } @@ -2099,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, @@ -2123,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)*; @@ -2149,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) { - let ($($name,)*) = _fetch; + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table, _table_id: TableId) { + 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); } + unsafe { $name::set_table(&mut $name.0, $state, _table, _table_id); } + _fetch.1 |= ($name::is_shared(&$name.0) as usize & 1) << $n; } )* } @@ -2248,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) }), )*) } } @@ -2262,7 +2713,7 @@ macro_rules! impl_anytuple_fetch { }; } -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_tuple_query_data, 0, @@ -2270,7 +2721,7 @@ all_tuples!( F, S ); -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_anytuple_fetch, 0, @@ -2314,7 +2765,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 +2842,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) {} @@ -2469,16 +2931,17 @@ impl StorageSwitch { /// Fetches the internal value from the variant that corresponds to the /// component's [`StorageType`]. + /// 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) -> R, sparse_set: impl FnOnce(S) -> 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) }, + // SAFETY: C::STORAGE_TYPE == StorageType::SparseSet + 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 e4e1f0fd668d3..3a6ef245d7fe3 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); } } )* } @@ -530,7 +532,7 @@ macro_rules! impl_tuple_query_filter { entity: Entity, table_row: TableRow ) -> bool { - let ($($name,)*) = fetch; + let ($($name,)*) = &mut fetch.0; // SAFETY: The invariants are upheld by the caller. true $(&& unsafe { $name::filter_fetch($name, entity, table_row) })* } @@ -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..8a50521e3f12e 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, @@ -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, @@ -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,13 +192,15 @@ 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] + #[inline(always)] pub(super) unsafe fn fold_over_table_range( &mut self, 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(); @@ -248,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, @@ -317,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, @@ -898,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, @@ -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/state.rs b/crates/bevy_ecs/src/query/state.rs index e9a00f4646e1f..6a32189b8b216 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::{InheritedArchetypeComponent, 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,19 @@ 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 = 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)); + + 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 +697,26 @@ 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() { + archetype + .inherited_components + .get(component_access.index()) + .map(InheritedArchetypeComponent::archetype_component_id) + } else { + None + } + }) + { match component_access { ComponentAccessKind::Archetypal(_) => {} ComponentAccessKind::Shared(_) => { @@ -699,12 +728,11 @@ impl QueryState { } } }); - return; } for (component_id, archetype_component_id) in - archetype.components_with_archetype_component_id() + archetype.components_with_inherited_and_archetype_component_id() { if self .component_access diff --git a/crates/bevy_ecs/src/query/world_query.rs b/crates/bevy_ecs/src/query/world_query.rs index da147770e0fcf..9753a143333cd 100644 --- a/crates/bevy_ecs/src/query/world_query.rs +++ b/crates/bevy_ecs/src/query/world_query.rs @@ -1,11 +1,13 @@ +use core::marker::PhantomData; + 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; +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`] @@ -98,8 +100,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). @@ -130,10 +138,15 @@ pub unsafe trait WorldQuery { state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool, ) -> bool; + + #[inline(always)] + fn is_shared<'w>(_fetch: &Self::Fetch<'w>) -> bool { + false + } } macro_rules! impl_tuple_world_query { - ($(#[$meta:meta])* $(($name: ident, $state: ident)),*) => { + ($(#[$meta:meta])* $(($n:tt, $name: ident, $state: ident)),*) => { #[expect( clippy::allow_attributes, @@ -158,48 +171,48 @@ 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> = (($($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; - ($( - $name::shrink_fetch($name), - )*) + let ($($name,)*) = fetch.0; + (($( $name::shrink_fetch($name),)*), fetch.1) } - #[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) },)*) + (($(unsafe { $name::init_fetch(world, $name, last_run, this_run) },)*), 0) } 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.0; let ($($state,)*) = state; // SAFETY: The invariants are upheld by the caller. $(unsafe { $name::set_archetype($name, $state, archetype, table); })* + $(if $name::is_shared($name) {fetch.1 |= 1 << $n;} )* } - #[inline] - unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { - let ($($name,)*) = fetch; + #[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.0; 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); })* + $(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);)* @@ -219,7 +232,7 @@ macro_rules! impl_tuple_world_query { }; } -all_tuples!( +all_tuples_enumerated!( #[doc(fake_variadic)] impl_tuple_world_query, 0, 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 0f80b77f513c4..ef039055093c4 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -2,8 +2,10 @@ 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, }; use alloc::{boxed::Box, vec, vec::Vec}; use bevy_platform::collections::HashMap; @@ -31,7 +33,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 +150,7 @@ impl TableRow { pub(crate) struct TableBuilder { columns: SparseSet, capacity: usize, + has_inherited_components: bool, } impl TableBuilder { @@ -156,6 +159,7 @@ impl TableBuilder { Self { columns: SparseSet::with_capacity(column_capacity), capacity, + has_inherited_components: false, } } @@ -166,6 +170,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 +182,8 @@ impl TableBuilder { Table { columns: self.columns.into_immutable(), entities: Vec::with_capacity(self.capacity), + has_inherited_components: self.has_inherited_components, + inherited_components: Default::default(), } } } @@ -194,6 +203,8 @@ impl TableBuilder { pub struct Table { columns: ImmutableSparseSet, entities: Vec, + has_inherited_components: bool, + pub(crate) inherited_components: HashMap, } struct AbortOnPanic; @@ -630,6 +641,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(always)] + 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(); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 4cb6d61bc0e9a..9ea0b4c007a76 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::MutComponent, observer::{Observer, TriggerTargets}, resource::Resource, schedule::ScheduleLabel, @@ -136,6 +137,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 +145,7 @@ const _: () = { <__StructFieldsAlias<'_, '_> as bevy_ecs::system::SystemParam>::new_archetype( &mut state.state, archetype, + inherited_components, system_meta, ); }; @@ -2078,7 +2081,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(MutComponent) + 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/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index b0bbe187ed81b..2ab83e3df3740 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) { 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/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 02c12fe6a3560..a5021442d52e5 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::MutComponent, 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 a9887c5248673..4a60feb75945a 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, MutComponent}, observer::Observer, query::{Access, ReadOnlyQueryData}, relationship::RelationshipHookMode, @@ -585,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() } } @@ -597,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 @@ -608,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() } } @@ -620,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 @@ -1342,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() } @@ -1438,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() } @@ -1450,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() } } @@ -1467,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() } } @@ -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,7 @@ impl<'w> EntityWorldMut<'w> { } let old_table_row = remove_result.table_row; let old_table_id = old_archetype.table_id(); + let new_archetype = &mut archetypes[new_archetype_id]; let new_location = if old_table_id == new_archetype.table_id() { @@ -2159,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 { @@ -2185,6 +2216,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 +2280,8 @@ impl<'w> EntityWorldMut<'w> { &mut world.entities, &mut world.archetypes, &mut world.storages, + &mut world.inherited_components, + &world.components, new_archetype_id, ); @@ -3023,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()); @@ -3252,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) -> MutComponent<'_, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut::().unwrap() } @@ -3281,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) -> MutComponent<'a, T> { // This shouldn't panic because if we have an OccupiedEntry the component must exist. self.entity_world.get_mut().unwrap() } @@ -3796,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) @@ -3809,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 @@ -3824,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) @@ -4309,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 9bd8d699c6f21..2d29690c81537 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -18,6 +18,10 @@ 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; @@ -51,6 +55,7 @@ use crate::{ }, entity_disabling::DefaultQueryFilters, event::{Event, EventId, Events, SendBatchIds}, + inheritance::InheritFrom, observer::Observers, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, @@ -100,6 +105,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 +133,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 +174,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::(); } @@ -1283,7 +1296,7 @@ impl World { pub fn get_mut>( &mut self, entity: Entity, - ) -> Option> { + ) -> Option> { self.get_entity_mut(entity).ok()?.into_mut() } @@ -2947,11 +2960,16 @@ impl World { self.components_registrator().apply_queued_registrations(); } + pub(crate) fn flush_shared_mutations(&mut self) { + InheritedComponents::apply_queued_shared_mutations(self); + } + /// Flushes queued entities and commands. /// /// 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_commands(); diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index b46b4a154b359..b0613325f1a2f 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -7,6 +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, MutComponent, MutComponentSharedData, + }, observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, ReadOnlyQueryData}, @@ -17,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 @@ -296,6 +306,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,8 +840,9 @@ impl<'w> UnsafeEntityCell<'w> { T::STORAGE_TYPE, self.entity, 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), @@ -894,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 @@ -907,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( @@ -927,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::())?; @@ -943,13 +962,21 @@ impl<'w> UnsafeEntityCell<'w> { T::STORAGE_TYPE, self.entity, self.location, + true, + ) + .map( + |(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, + is_shared, + table_row: self.location.table_row, + 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()), - }) } } @@ -974,7 +1001,8 @@ impl<'w> UnsafeEntityCell<'w> { .get(location.archetype_id) .debug_checked_unwrap() }; - if Q::matches_component_set(&state, &|id| archetype.contains(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( @@ -1066,8 +1094,9 @@ impl<'w> UnsafeEntityCell<'w> { info.storage_type(), self.entity, 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( @@ -1114,8 +1143,9 @@ impl<'w> UnsafeEntityCell<'w> { info.storage_type(), self.entity, 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( @@ -1202,6 +1232,29 @@ unsafe fn get_component( } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get(entity), } + .or_else(|| { + let archetype = world.archetypes().get(location.archetype_id)?; + 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) + } + } + }) } /// Get an untyped pointer to a particular [`Component`] and its [`ComponentTicks`] @@ -1218,33 +1271,109 @@ unsafe fn get_component_and_ticks( storage_type: StorageType, entity: Entity, location: EntityLocation, + include_inherited: bool, ) -> Option<( Ptr<'_>, TickCells<'_>, MaybeLocation<&UnsafeCell<&'static Location<'static>>>, + bool, + Option<&MutComponentSharedData>, )> { match storage_type { StorageType::Table => { 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), } + .map(|(a, b, c)| (a, b, c, false, None)) + .or_else(|| { + if !include_inherited { + return None; + } + let archetype = world.archetypes().get(location.archetype_id)?; + match archetype.inherited_components.get(&component_id)? { + &InheritedArchetypeComponent::Table { + table_id, + 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, + table_row, + ..location + }; + let table = world.fetch_table(location)?; + table + .get_component(component_id, location.table_row) + .and_then(|ptr| { + Some(( + 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()), + true, + Some( + world + .inherited_components() + .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) + .and_then(|(a, b, c)| { + Some(( + a, + b, + c, + true, + Some( + world + .inherited_components() + .get_shared_sparse_component_data( + world.components().get_info(component_id)?, + location.archetype_id, + ), + ), + )) + }), + } + }) } /// Get an untyped pointer to the [`ComponentTicks`] on a particular [`Entity`] @@ -1271,6 +1400,29 @@ 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)?; + 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) + } + } + }) } impl ContainsEntity for UnsafeEntityCell<'_> { 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_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index ce0408833366c..ee780a42eaedf 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -289,7 +289,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}, }; @@ -338,9 +338,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( @@ -438,9 +441,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( diff --git a/crates/bevy_text/src/text_access.rs b/crates/bevy_text/src/text_access.rs index 7aafa28ef63e2..60b38b794d7de 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::MutComponent, 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, + Mut, + MutComponent, + MutComponent, + )> { // Root if index == 0 { let (text, font, color) = self.roots.get_mut(root_entity).ok()?; @@ -326,12 +333,20 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// 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) } @@ -346,14 +361,14 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { /// 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) -> 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) -> Mut { + pub fn color(&mut self, root_entity: Entity, index: usize) -> MutComponent { 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, + Mut, + MutComponent, + MutComponent, + ), ) { self.for_each_until(root_entity, |a, b, c, d, e| { (callback)(a, b, c, d, e); @@ -377,7 +398,11 @@ impl<'w, 's, R: TextRoot> TextWriter<'w, 's, R> { } /// 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(MutComponent), + ) { self.for_each(root_entity, |_, _, _, font, _| { (callback)(font); }); @@ -387,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(Mut), + mut callback: impl FnMut(MutComponent), ) { self.for_each(root_entity, |_, _, _, _, color| { (callback)(color); @@ -401,7 +426,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, + Mut, + MutComponent, + MutComponent, + ) -> bool, ) { // Root let Ok((text, font, color)) = self.roots.get_mut(root_entity) else { diff --git a/crates/bevy_transform/src/systems.rs b/crates/bevy_transform/src/systems.rs index ecf66512715e9..9b70b1db79a3a 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::MutComponent, 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: 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 0153fa954c406..0b2def341c027 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::MutComponent, 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: MutComponent, + mut text_flags: MutComponent, + mut computed: MutComponent, 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: 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 33ad693c5ab4a..a3dd42b6247fb 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::MutComponent, prelude::*, system::SystemState, world::FromWorld, @@ -954,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 Mut<'_, Window>, + window: &mut MutComponent<'_, Window>, size: PhysicalSize, window_resized: &mut EventWriter, ) { @@ -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 MutComponent<'_, Window>, scale_factor: f64, window_backend_scale_factor_changed: &mut EventWriter, window_scale_factor_changed: &mut EventWriter,