From 44a13bfe903aced09fc36d26a9864de7f5e37cba Mon Sep 17 00:00:00 2001 From: Cameron <51241057+maniwani@users.noreply.github.com> Date: Tue, 15 Aug 2023 11:33:45 -0700 Subject: [PATCH] everything --- crates/bevy_ecs/macros/src/lib.rs | 26 +- crates/bevy_ecs/src/archetype.rs | 1700 ++++++++++++++++---- crates/bevy_ecs/src/bundle.rs | 693 ++++---- crates/bevy_ecs/src/component.rs | 32 +- crates/bevy_ecs/src/entity/mod.rs | 22 +- crates/bevy_ecs/src/lib.rs | 28 +- crates/bevy_ecs/src/maps.rs | 374 +++++ crates/bevy_ecs/src/new_archetype.rs | 529 ++++++ crates/bevy_ecs/src/storage/table.rs | 130 +- crates/bevy_ecs/src/system/commands/mod.rs | 4 +- crates/bevy_ecs/src/world/entity_ref.rs | 503 +----- crates/bevy_ecs/src/world/mod.rs | 313 +++- crates/bevy_ecs/src/world/spawn_batch.rs | 2 +- crates/bevy_utils/Cargo.toml | 2 + crates/bevy_utils/src/lib.rs | 2 + 15 files changed, 3099 insertions(+), 1261 deletions(-) create mode 100644 crates/bevy_ecs/src/maps.rs create mode 100644 crates/bevy_ecs/src/new_archetype.rs diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b4dde955dd388..e2f51b243ed21 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -74,8 +74,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { .collect::>(); let mut field_component_ids = Vec::new(); - let mut field_get_components = Vec::new(); - let mut field_from_components = Vec::new(); + let mut field_map_components = Vec::new(); + let mut field_read_components = Vec::new(); for ((field_type, field_kind), field) in field_type.iter().zip(field_kind.iter()).zip(field.iter()) { @@ -84,16 +84,16 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { field_component_ids.push(quote! { <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids); }); - field_get_components.push(quote! { - self.#field.get_components(&mut *func); + field_map_components.push(quote! { + self.#field.map_components(&mut *func); }); - field_from_components.push(quote! { - #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(ctx, &mut *func), + field_read_components.push(quote! { + #field: <#field_type as #ecs_path::bundle::Bundle>::read_components(ctx, &mut *func), }); } BundleFieldKind::Ignore => { - field_from_components.push(quote! { + field_read_components.push(quote! { #field: ::std::default::Default::default(), }); } @@ -105,8 +105,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { TokenStream::from(quote! { // SAFETY: - // - ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order - // - `Bundle::get_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass + // - ComponentId is returned in field-definition-order. [read_components] and [map_components] use field-definition-order + // - `Bundle::map_components` is exactly once for each member. Rely's on the Component -> Bundle implementation to properly pass // the correct `StorageType` into the callback. unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause { fn component_ids( @@ -118,22 +118,22 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { } #[allow(unused_variables, non_snake_case)] - unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self + unsafe fn read_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self where __F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_> { Self { - #(#field_from_components)* + #(#field_read_components)* } } #[allow(unused_variables)] #[inline] - fn get_components( + unsafe fn map_components( self, func: &mut impl FnMut(#ecs_path::component::StorageType, #ecs_path::ptr::OwningPtr<'_>) ) { - #(#field_get_components)* + #(#field_map_components)* } } }) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index bd2ceb5dcc582..34e64bfe36e3e 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -19,65 +19,164 @@ //! [`Table`]: crate::storage::Table //! [`World::archetypes`]: crate::world::World::archetypes +use bevy_utils::generational_arena::Arena; +use bevy_utils::{default, HashMap, HashSet}; + +use crate::storage::TableColumn; use crate::{ - bundle::BundleId, - component::{ComponentId, StorageType}, - entity::{Entity, EntityLocation}, - storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow}, + component::{ComponentId, Components, StorageType}, + entity::{Entities, Entity, EntityLocation}, + storage::{ + ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, Storages, Table, TableId, + TableRow, + }, }; use std::{ hash::Hash, ops::{Index, IndexMut}, }; -/// An opaque location within a [`Archetype`]. +// PR 1 +// TODO: Refactoring +// - component -> archetype index +// - change graph to support destroying components and archetypes + +// PR 2 +// TODO: Hooks + Observers + +// PR 3 +// TODO: Query API + +// PR 4 +// TODO: Relationships +// - Add a wildcard (*) component. +// - Add handling for wildcarded pairs (T, *), (*, T), (*, *) and finding their matching archetypes. +// - Limit maximum `Entity` generation to (2^31 - 1) (MSB == 0). +// - Add field to `EntityMeta` to detect when there's need to destroy components/relations. +// - NOTE: Sort the IDs in ArchetypeDef. +// - NOTE: Mark and sweep for archetype graph updates. +// - + +/// Identifies something that an [`Entity`] can have. An abstraction over [`Entity`] and [`Pair`]. /// -/// This can be used in conjunction with [`ArchetypeId`] to find the exact location -/// of an [`Entity`] within a [`World`]. An entity's archetype and index can be -/// retrieved via [`Entities::get`]. +/// Archetypes can consist of both components and relationships, which are identified by [`Entity`] +/// and [`Pair`] values, respectively. /// -/// [`World`]: crate::world::World -/// [`Entities::get`]: crate::entity::Entities -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation -#[repr(transparent)] -pub struct ArchetypeRow(u32); +/// **Note:** The set of valid [`Entity`] values and the set of valid [`Pair`] values are disjoint. +#[repr(C, align(4))] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub(crate) struct Id { + hi: u32, + lo: u32, +} -impl ArchetypeRow { - pub const INVALID: ArchetypeRow = ArchetypeRow(u32::MAX); +pub(crate) enum IdEnum { + Entity(Entity), + Pair(Pair), +} - /// Creates a `ArchetypeRow`. - pub const fn new(index: usize) -> Self { - Self(index as u32) +impl Id { + fn msb(&self) -> u32 { + self.hi.reverse_bits() & 1 } - /// Gets the index of the row. + /// Returns `true` if this is an [`Entity`]. #[inline] - pub const fn index(self) -> usize { - self.0 as usize + pub fn is_entity(&self) -> bool { + self.msb() == 0 + } + + /// Returns `true` if this is a [`Pair`]. + #[inline] + pub fn is_pair(&self) -> bool { + !self.is_entity() + } + + /// Returns an enum. + pub fn to_enum(self) -> IdEnum { + if self.is_entity() { + IdEnum::Entity(Entity { + generation: self.hi, + index: self.lo, + }) + } else { + IdEnum::Pair(Pair { + lhs: self.hi, + rhs: self.lo, + }) + } + } +} + +impl From for Id { + fn from(entity: Entity) -> Self { + Self { + hi: entity.generation, + lo: entity.index, + } + } +} + +impl From for Id { + fn from(pair: Pair) -> Self { + Self { + hi: pair.lhs, + lo: pair.rhs, + } + } +} + +/// A concatenated pair of [`Entity`] indices used to uniquely identify some kind of relationship +/// between them. +/// +/// **Note:** As they are based on `Entity` values, `Pair` values should only be considered valid +/// in their original world. Assume that the `Pair` from one world will never refer to the same +/// pair in any other world. +#[repr(C, align(4))] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Pair { + lhs: u32, + rhs: u32, +} + +impl Pair { + pub fn new(lhs: u32, rhs: u32) -> Self { + assert!( + (lhs.reverse_bits() & 1) == 0, + "The `lhs` index is too large. The MSB is used to distinguish `Entity` and `Pair` values.", + ); + Self { lhs, rhs } + } + + pub fn lhs(&self) -> u32 { + self.lhs + } + + pub fn rhs(&self) -> u32 { + self.rhs } } -/// An opaque unique ID for a single [`Archetype`] within a [`World`]. +/// A unique key representing an [`Archetype`]. /// -/// Archetype IDs are only valid for a given World, and are not globally unique. -/// Attempting to use an archetype ID on a world that it wasn't sourced from will -/// not return the archetype with the same components. The only exception to this is -/// [`EMPTY`] which is guaranteed to be identical for all Worlds. +/// **Note:** `ArchetypeId` values are not globally unique and should only be considered valid in +/// their original world. Assume that an `ArchetypeId` (that isn't a constant like [`EMPTY`]) can +/// never refer to the same archetype in any other world. /// /// [`World`]: crate::world::World /// [`EMPTY`]: crate::archetype::ArchetypeId::EMPTY -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] // SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation #[repr(transparent)] pub struct ArchetypeId(u32); impl ArchetypeId { - /// The ID for the [`Archetype`] without any components. + /// The ID for the [`Archetype`] with no components. pub const EMPTY: ArchetypeId = ArchetypeId(0); - /// # Safety: + /// # Safety /// - /// This must always have an all-1s bit pattern to ensure soundness in fast entity id space allocation. + /// This must always have an all-1s bit pattern to ensure the soundness of + /// fast [`Entity`] space allocation. pub const INVALID: ArchetypeId = ArchetypeId(u32::MAX); #[inline] @@ -91,167 +190,90 @@ impl ArchetypeId { } } -#[derive(Copy, Clone)] -pub(crate) enum ComponentStatus { - Added, - Mutated, -} - -pub(crate) struct AddBundle { - pub archetype_id: ArchetypeId, - pub bundle_status: Vec, -} - -/// This trait is used to report the status of [`Bundle`](crate::bundle::Bundle) components -/// being added to a given entity, relative to that entity's original archetype. -/// See [`crate::bundle::BundleInfo::write_components`] for more info. -pub(crate) trait BundleComponentStatus { - /// Returns the Bundle's component status for the given "bundle index" - /// - /// # Safety - /// Callers must ensure that index is always a valid bundle index for the - /// Bundle associated with this [`BundleComponentStatus`] - unsafe fn get_status(&self, index: usize) -> ComponentStatus; -} - -impl BundleComponentStatus for AddBundle { - #[inline] - unsafe fn get_status(&self, index: usize) -> ComponentStatus { - // SAFETY: caller has ensured index is a valid bundle index for this bundle - *self.bundle_status.get_unchecked(index) - } -} - -pub(crate) struct SpawnBundleStatus; - -impl BundleComponentStatus for SpawnBundleStatus { - #[inline] - unsafe fn get_status(&self, _index: usize) -> ComponentStatus { - // Components added during a spawn call are always treated as added - ComponentStatus::Added - } -} - -/// Archetypes and bundles form a graph. Adding or removing a bundle moves -/// an [`Entity`] to a new [`Archetype`]. +/// A unique key for the [`Component`] data in one [`Archetype`]. /// -/// [`Edges`] caches the results of these moves. Each archetype caches -/// the result of a structural alteration. This can be used to monitor the -/// state of the archetype graph. +/// In database terms, these are composite keys representing a [many-to-many relationship] between +/// archetypes and components. A component type has only one [`ComponentId`] and an archetype has +/// only one [`ArchetypeId`], but many archetypes can have similar components, so each occurence +/// is given a unique `ArchetypeComponentId`. This information can be leveraged by system schedulers +/// to opportunistically run systems in parallel. For example, `Query<&mut A, With>` and +/// `Query<&mut A, Without>` can run at the same time because, even though both mutably borrow +/// `A`, they will always borrow from different archetypes. /// -/// Note: This type only contains edges the [`World`] has already traversed. -/// If any of functions return `None`, it doesn't mean there is guaranteed -/// not to be a result of adding or removing that bundle, but rather that -/// operation that has moved an entity along that edge has not been performed -/// yet. +/// To make the access model simpler, every [`Resource`] type is also assigned an +/// `ArchetypeComponentId` (but only one because resource types are not actually included in +/// archetypes). /// +/// **Note:** `ArchetypeComponentId` values are not globally unique and should only be considered +/// valid in their original world. Assume that an `ArchetypeComponentId` from one world can never +/// refer to the same one in any other world. +/// +/// [`Component`]: crate::component::Component /// [`World`]: crate::world::World -#[derive(Default)] -pub struct Edges { - add_bundle: SparseArray, - remove_bundle: SparseArray>, - remove_bundle_intersection: SparseArray>, -} +/// [`Resource`]: crate::system::Resource +/// [many-to-many relationship]: https://en.wikipedia.org/wiki/Many-to-many_(data_model) +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub struct ArchetypeComponentId(usize); -impl Edges { - /// Checks the cache for the target archetype when adding a bundle to the - /// source archetype. For more information, see [`EntityMut::insert`]. - /// - /// If this returns `None`, it means there has not been a transition from - /// the source archetype via the provided bundle. - /// - /// [`EntityMut::insert`]: crate::world::EntityMut::insert +impl ArchetypeComponentId { #[inline] - pub fn get_add_bundle(&self, bundle_id: BundleId) -> Option { - self.get_add_bundle_internal(bundle_id) - .map(|bundle| bundle.archetype_id) + pub(crate) const fn new(index: usize) -> Self { + Self(index) } +} - /// Internal version of `get_add_bundle` that fetches the full `AddBundle`. +impl SparseSetIndex for ArchetypeComponentId { #[inline] - pub(crate) fn get_add_bundle_internal(&self, bundle_id: BundleId) -> Option<&AddBundle> { - self.add_bundle.get(bundle_id) + fn sparse_set_index(&self) -> usize { + self.0 } - /// Caches the target archetype when adding a bundle to the source archetype. - /// For more information, see [`EntityMut::insert`]. - /// - /// [`EntityMut::insert`]: crate::world::EntityMut::insert - #[inline] - pub(crate) fn insert_add_bundle( - &mut self, - bundle_id: BundleId, - archetype_id: ArchetypeId, - bundle_status: Vec, - ) { - self.add_bundle.insert( - bundle_id, - AddBundle { - archetype_id, - bundle_status, - }, - ); + fn get_sparse_set_index(value: usize) -> Self { + Self(value) } +} - /// Checks the cache for the target archetype when removing a bundle to the - /// source archetype. For more information, see [`EntityMut::remove`]. - /// - /// If this returns `None`, it means there has not been a transition from - /// the source archetype via the provided bundle. - /// - /// If this returns `Some(None)`, it means that the bundle cannot be removed - /// from the source archetype. - /// - /// [`EntityMut::remove`]: crate::world::EntityMut::remove - #[inline] - pub fn get_remove_bundle(&self, bundle_id: BundleId) -> Option> { - self.remove_bundle.get(bundle_id).cloned() - } +/// Metadata for a [`Component`] in an [`Archetype`]. +/// +/// [`Component`]: crate::component::Component +struct ArchetypeComponentInfo { + storage_type: StorageType, + archetype_component_id: ArchetypeComponentId, +} - /// Caches the target archetype when removing a bundle to the source archetype. - /// For more information, see [`EntityMut::remove`]. - /// - /// [`EntityMut::remove`]: crate::world::EntityMut::remove - #[inline] - pub(crate) fn insert_remove_bundle( - &mut self, - bundle_id: BundleId, - archetype_id: Option, - ) { - self.remove_bundle.insert(bundle_id, archetype_id); - } +/// A location within an [`Archetype`]. +/// +/// This can be used in conjunction with [`ArchetypeId`] to find the exact location of an +/// [`Entity`]'s data in the [`World`]. An entity's archetype and index can be retrieved via +/// [`Entities::get`]. +/// +/// [`World`]: crate::world::World +/// [`Entities::get`]: crate::entity::Entities +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation +#[repr(transparent)] +pub struct ArchetypeRow(u32); - /// Checks the cache for the target archetype when removing a bundle to the - /// source archetype. For more information, see [`EntityMut::remove_intersection`]. - /// - /// If this returns `None`, it means there has not been a transition from - /// the source archetype via the provided bundle. +impl ArchetypeRow { + /// # Safety: /// - /// [`EntityMut::remove_intersection`]: crate::world::EntityMut::remove_intersection - #[inline] - pub fn get_remove_bundle_intersection( - &self, - bundle_id: BundleId, - ) -> Option> { - self.remove_bundle_intersection.get(bundle_id).cloned() + /// This must always have an all-1s bit pattern to ensure soundness in fast [`Entity`] ID space + /// allocation. + pub const INVALID: ArchetypeRow = ArchetypeRow(u32::MAX); + + /// Creates a `ArchetypeRow`. + pub const fn new(index: usize) -> Self { + Self(index as u32) } - /// Caches the target archetype when removing a bundle to the source archetype. - /// For more information, see [`EntityMut::remove_intersection`]. - /// - /// [`EntityMut::remove_intersection`]: crate::world::EntityMut::remove_intersection + /// Gets the index of the row. #[inline] - pub(crate) fn insert_remove_bundle_intersection( - &mut self, - bundle_id: BundleId, - archetype_id: Option, - ) { - self.remove_bundle_intersection - .insert(bundle_id, archetype_id); + pub const fn index(self) -> usize { + self.0 as usize } } -/// Metadata about an [`Entity`] in a [`Archetype`]. +/// Metadata used to locate the data of a particular [`Entity`] in an [`Archetype`]. pub struct ArchetypeEntity { entity: Entity, table_row: TableRow, @@ -278,25 +300,26 @@ pub(crate) struct ArchetypeSwapRemoveResult { pub(crate) table_row: TableRow, } -/// Internal metadata for a [`Component`] within a given [`Archetype`]. -/// -/// [`Component`]: crate::component::Component -struct ArchetypeComponentInfo { - storage_type: StorageType, - archetype_component_id: ArchetypeComponentId, +/// A hashable representation of the set of components that uniquely define an [`Archetype`]. +#[derive(Hash, PartialEq, Eq)] +struct ArchetypeDef { + table_components: Box<[ComponentId]>, + sparse_set_components: Box<[ComponentId]>, } -/// Metadata for a single archetype within a [`World`]. +/// Metadata for a group of entities sharing a specific set of components (an archetype) +/// in a [`World`]. /// -/// For more information, see the *[module level documentation]*. +/// For more information, see the [module-level documentation]. /// /// [`World`]: crate::world::World -/// [module level documentation]: crate::archetype +/// [module-level documentation]: crate::archetype pub struct Archetype { + edges: ArchetypeEdges, id: ArchetypeId, table_id: TableId, - edges: Edges, entities: Vec, + // TODO: reference to leaked archetype def? components: ImmutableSparseSet, } @@ -310,6 +333,7 @@ impl Archetype { let (min_table, _) = table_components.size_hint(); let (min_sparse, _) = sparse_set_components.size_hint(); let mut components = SparseSet::with_capacity(min_table + min_sparse); + // TODO: debug assert sorted for (component_id, archetype_component_id) in table_components { components.insert( component_id, @@ -320,7 +344,9 @@ impl Archetype { ); } + // TODO: debug assert sorted for (component_id, archetype_component_id) in sparse_set_components { + debug_assert!(component_id.index() > ); components.insert( component_id, ArchetypeComponentInfo { @@ -338,30 +364,48 @@ impl Archetype { } } - /// Fetches the ID for the archetype. + /// Returns the [`ArchetypeId`] of the archetype. #[inline] pub fn id(&self) -> ArchetypeId { self.id } - /// Fetches the archetype's [`Table`] ID. - /// - /// [`Table`]: crate::storage::Table + /// Returns the [`TableId`] of the [`Table`] that holds the component data of the entities + /// in the archetype. #[inline] pub fn table_id(&self) -> TableId { self.table_id } + /// Returns the number of entities with the archetype. + #[inline] + pub fn len(&self) -> usize { + self.entities.len() + } + + /// Returns `true` if there are no entities with the archetype. + #[inline] + pub fn is_empty(&self) -> bool { + self.entities.is_empty() + } + #[inline] pub fn entities(&self) -> &[ArchetypeEntity] { &self.entities } - /// Gets an iterator of all of the components stored in [`Table`]s. + /// Returns an iterator over the components in the archetype. /// - /// All of the IDs are unique. + /// **Note:** The iterator will never yield more than one of each [`ComponentId`]. + #[inline] + pub fn components(&self) -> impl Iterator + '_ { + self.components.indices() + } + + /// Returns an iterator over the [`Table`](StorageType::Table) components + /// in the archetype. /// - /// [`Table`]: crate::storage::Table + /// **Note:** The iterator will never yield more than one of each [`ComponentId`]. #[inline] pub fn table_components(&self) -> impl Iterator + '_ { self.components @@ -370,11 +414,10 @@ impl Archetype { .map(|(id, _)| *id) } - /// Gets an iterator of all of the components stored in [`ComponentSparseSet`]s. - /// - /// All of the IDs are unique. + /// Returns an iterator over the [`SparseSet`](StorageType::SparseSet) components + /// in the archetype. /// - /// [`ComponentSparseSet`]: crate::storage::ComponentSparseSet + /// **Note:** The iterator will never yield more than one of each [`ComponentId`]. #[inline] pub fn sparse_set_components(&self) -> impl Iterator + '_ { self.components @@ -383,47 +426,36 @@ impl Archetype { .map(|(id, _)| *id) } - /// Gets an iterator of all of the components in the archetype. - /// - /// All of the IDs are unique. - #[inline] - pub fn components(&self) -> impl Iterator + '_ { - self.components.indices() - } - - /// Fetches a immutable reference to the archetype's [`Edges`], a cache of - /// archetypal relationships. + /// Returns a reference to the cached edges of the archetype. #[inline] - pub fn edges(&self) -> &Edges { + pub fn edges(&self) -> &ArchetypeEdges { &self.edges } - /// Fetches a mutable reference to the archetype's [`Edges`], a cache of - /// archetypal relationships. + /// Returns a mutable reference to the cached edges of the archetype. #[inline] - pub(crate) fn edges_mut(&mut self) -> &mut Edges { + pub(crate) fn edges_mut(&mut self) -> &mut ArchetypeEdges { &mut self.edges } - /// Fetches the row in the [`Table`] where the components for the entity at `index` + /// Returns the row in the [`Table`] where the components of the entity at `row` /// is stored. /// - /// An entity's archetype row can be fetched from [`EntityLocation::archetype_row`], which - /// can be retrieved from [`Entities::get`]. + /// An entity's archetype row can be fetched from [`archetype_row`], which + /// can be retrieved from [`get`]. /// /// # Panics - /// This function will panic if `index >= self.len()`. + /// This function will panic if `row >= self.len()`. /// /// [`Table`]: crate::storage::Table - /// [`EntityLocation::archetype_row`]: crate::entity::EntityLocation::archetype_row - /// [`Entities::get`]: crate::entity::Entities::get + /// [`archetype_row`]: crate::entity::EntityLocation::archetype_row + /// [`get`]: crate::entity::Entities::get #[inline] pub fn entity_table_row(&self, row: ArchetypeRow) -> TableRow { self.entities[row.index()].table_row } - /// Updates if the components for the entity at `index` can be found - /// in the corresponding table. + /// Updates if the components for the entity at `index` can be found in the corresponding table. /// /// # Panics /// This function will panic if `index >= self.len()`. @@ -432,11 +464,11 @@ impl Archetype { self.entities[row.index()].table_row = table_row; } - /// Allocates an entity to the archetype. + /// Add an entity to the archetype. /// /// # Safety - /// valid component values must be immediately written to the relevant storages - /// `table_row` must be valid + /// - Valid component values must be immediately written to the relevant storages. + /// - `table_row` must be valid. pub(crate) unsafe fn allocate( &mut self, entity: Entity, @@ -453,15 +485,21 @@ impl Archetype { } } + /// Reserves capacity for at least `additional` more entities to be inserted in the given + /// [`Archetype`]. The collection may reserve more space to speculatively avoid frequent + /// reallocations. After calling `reserve`, capacity will be greater than or equal to + /// `self.len() + additional`. Does nothing if capacity is already sufficient. pub(crate) fn reserve(&mut self, additional: usize) { self.entities.reserve(additional); } - /// Removes the entity at `index` by swapping it out. Returns the table row the entity is stored - /// in. + /// Removes the entity at `row` and returns the [`TableRow`] where its component data lives. + /// + /// The removed entity is replaced by the last entity in the archetype. /// /// # Panics - /// This function will panic if `index >= self.len()` + /// + /// Panics if `row` is out of bounds. pub(crate) fn swap_remove(&mut self, row: ArchetypeRow) -> ArchetypeSwapRemoveResult { let is_last = row.index() == self.entities.len() - 1; let entity = self.entities.swap_remove(row.index()); @@ -475,27 +513,15 @@ impl Archetype { } } - /// Gets the total number of entities that belong to the archetype. - #[inline] - pub fn len(&self) -> usize { - self.entities.len() - } - - /// Checks if the archetype has any entities. - #[inline] - pub fn is_empty(&self) -> bool { - self.entities.is_empty() - } - - /// Checks if the archetype contains a specific component. This runs in `O(1)` time. + /// Returns `true` if the archetype includes the given component. #[inline] pub fn contains(&self, component_id: ComponentId) -> bool { self.components.contains(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. + /// Returns the [`StorageType`] of the given component. + /// + /// Returns `None` if the component is not present in the archetype. #[inline] pub fn get_storage_type(&self, component_id: ComponentId) -> Option { self.components @@ -503,9 +529,9 @@ impl Archetype { .map(|info| info.storage_type) } - /// Fetches the corresponding [`ArchetypeComponentId`] for a component in the archetype. - /// Returns `None` if the component is not part of the archetype. - /// This runs in `O(1)` time. + /// Returns the corresponding [`ArchetypeComponentId`] for a component in the archetype. + /// + /// Returns `None` if the component is not present in the archetype. #[inline] pub fn get_archetype_component_id( &self, @@ -516,13 +542,13 @@ impl Archetype { .map(|info| info.archetype_component_id) } - /// Clears all entities from the archetype. + /// Removes all entities from the archetype. pub(crate) fn clear_entities(&mut self) { self.entities.clear(); } } -/// An opaque generational id that changes every time the set of [`Archetypes`] changes. +/// A counter that increments every time an [`Archetype`] is created or destroyed. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub struct ArchetypeGeneration(usize); @@ -538,77 +564,32 @@ impl ArchetypeGeneration { } } -#[derive(Hash, PartialEq, Eq)] -struct ArchetypeIdentity { - table_components: Box<[ComponentId]>, - sparse_set_components: Box<[ComponentId]>, -} - -/// An opaque unique joint ID for a [`Component`] in an [`Archetype`] within a [`World`]. -/// -/// A component may be present within multiple archetypes, but each component within -/// each archetype has its own unique `ArchetypeComponentId`. This is leveraged by the system -/// schedulers to opportunistically run multiple systems in parallel that would otherwise -/// conflict. For example, `Query<&mut A, With>` and `Query<&mut A, Without>` can run in -/// parallel as the matched `ArchetypeComponentId` sets for both queries are disjoint, even -/// though `&mut A` on both queries point to the same [`ComponentId`]. -/// -/// In SQL terms, these IDs are composite keys on a [many-to-many relationship] between archetypes -/// and components. Each component type will have only one [`ComponentId`], but may have many -/// [`ArchetypeComponentId`]s, one for every archetype the component is present in. Likewise, each -/// archetype will have only one [`ArchetypeId`] but may have many [`ArchetypeComponentId`]s, one -/// for each component that belongs to the archetype. -/// -/// Every [`Resource`] is also assigned one of these IDs. As resources do not belong to any -/// particular archetype, a resource's ID uniquely identifies it. -/// -/// These IDs are only valid within a given World, and are not globally unique. -/// Attempting to use an ID on a world that it wasn't sourced from will -/// not point to the same archetype nor the same component. -/// -/// [`Component`]: crate::component::Component -/// [`World`]: crate::world::World -/// [`Resource`]: crate::system::Resource -/// [many-to-many relationship]: https://en.wikipedia.org/wiki/Many-to-many_(data_model) -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct ArchetypeComponentId(usize); - -impl ArchetypeComponentId { - #[inline] - pub(crate) const fn new(index: usize) -> Self { - Self(index) - } -} - -impl SparseSetIndex for ArchetypeComponentId { - #[inline] - fn sparse_set_index(&self) -> usize { - self.0 - } - - fn get_sparse_set_index(value: usize) -> Self { - Self(value) - } -} - -/// The backing store of all [`Archetype`]s within a [`World`]. +/// Stores metadata for every [`Archetype`] in the [`World`]. /// -/// For more information, see the *[module level documentation]*. +/// For more information, see the [module-level documentation]. /// /// [`World`]: crate::world::World -/// [*module level documentation]: crate::archetype +/// [*module-level documentation]: crate::archetype pub struct Archetypes { + graph: ArchetypeGraph, pub(crate) archetypes: Vec, - pub(crate) archetype_component_count: usize, - archetype_ids: bevy_utils::HashMap, + archetype_ids: HashMap, + + component_records: HashMap, + event_records: HashMap, + + /// [`Access`](crate::query::Access)-related metadata for scheduler parallelism. + access_ids: Arena, } impl Archetypes { pub(crate) fn new() -> Self { let mut archetypes = Archetypes { archetypes: Vec::new(), - archetype_ids: Default::default(), - archetype_component_count: 0, + archetype_ids: default(), + component_records: default(), + graph: default(), + access_ids: default(), }; archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new()); archetypes @@ -619,14 +600,14 @@ impl Archetypes { ArchetypeGeneration(self.archetypes.len()) } - /// Fetches the total number of [`Archetype`]s within the world. + /// Returns the number of archetypes. #[inline] #[allow(clippy::len_without_is_empty)] // the internal vec is never empty. pub fn len(&self) -> usize { self.archetypes.len() } - /// Fetches an immutable reference to the archetype without any components. + /// Returns a reference to the [`Archetype`] with no components. /// /// Shorthand for `archetypes.get(ArchetypeId::EMPTY).unwrap()` #[inline] @@ -635,7 +616,7 @@ impl Archetypes { unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) } } - /// Fetches an mutable reference to the archetype without any components. + /// Returns a mutable reference to the [`Archetype`] with no components. #[inline] pub(crate) fn empty_mut(&mut self) -> &mut Archetype { // SAFETY: empty archetype always exists @@ -645,65 +626,93 @@ impl Archetypes { } } - /// Fetches an immutable reference to an [`Archetype`] using its - /// ID. Returns `None` if no corresponding archetype exists. + /// Returns a reference to the [`Archetype`], if it exists. #[inline] pub fn get(&self, id: ArchetypeId) -> Option<&Archetype> { self.archetypes.get(id.index()) } + /// Returns a mutable reference to the [`Archetype`], if it exists. + #[inline] + pub fn get_mut(&mut self, id: ArchetypeId) -> Option<&mut Archetype> { + self.archetypes.get_mut(id.index()) + } + #[inline] - pub(crate) fn get_2_mut( + pub(crate) unsafe fn get_many_unchecked_mut( &mut self, - a: ArchetypeId, - b: ArchetypeId, - ) -> (&mut Archetype, &mut Archetype) { - if a.index() > b.index() { - let (b_slice, a_slice) = self.archetypes.split_at_mut(a.index()); - (&mut a_slice[0], &mut b_slice[b.index()]) - } else { - let (a_slice, b_slice) = self.archetypes.split_at_mut(b.index()); - (&mut a_slice[a.index()], &mut b_slice[0]) - } + ids: [ArchetypeId; N], + ) -> [&'_ mut Archetype; N] { + let mut indices = [usize::MAX; N]; + ids.iter() + .enumerate() + .for_each(|(i, id)| indices[i] = id.index()); + self.archetypes.get_many_unchecked_mut(indices) + } + + #[inline] + pub(crate) fn get_many_mut( + &mut self, + ids: [ArchetypeId; N], + ) -> Option<[&'_ mut Archetype; N]> { + let mut indices = [usize::MAX; N]; + ids.iter() + .enumerate() + .for_each(|(i, id)| indices[i] = id.index()); + self.archetypes.get_many_mut(indices) } - /// Returns a read-only iterator over all archetypes. + /// Returns an iterator over all archetypes. #[inline] pub fn iter(&self) -> impl Iterator { self.archetypes.iter() } - /// Gets the archetype id matching the given inputs or inserts a new one if it doesn't exist. - /// `table_components` and `sparse_set_components` must be sorted + /// Returns a mutable iterator over all archetypes. + #[inline] + pub fn iter_mut(&mut self) -> impl Iterator { + self.archetypes.iter_mut() + } + + /// Returns the [`ArchetypeId`] of the set of components, creating one if it doesn't exist. /// /// # Safety - /// [`TableId`] must exist in tables + /// - The given table must exist. + /// - `table_components` and `sparse_set_components` must be sorted. pub(crate) fn get_id_or_insert( &mut self, table_id: TableId, table_components: Vec, sparse_set_components: Vec, ) -> ArchetypeId { - let archetype_identity = ArchetypeIdentity { + let archetype_def = ArchetypeDef { sparse_set_components: sparse_set_components.clone().into_boxed_slice(), table_components: table_components.clone().into_boxed_slice(), }; let archetypes = &mut self.archetypes; let archetype_component_count = &mut self.archetype_component_count; + + // TODO: ArchetypeCreated hook *self .archetype_ids - .entry(archetype_identity) + .entry(archetype_def) .or_insert_with(move || { let id = ArchetypeId::new(archetypes.len()); let table_start = *archetype_component_count; + *archetype_component_count += table_components.len(); + let table_archetype_components = (table_start..*archetype_component_count).map(ArchetypeComponentId); + let sparse_start = *archetype_component_count; + *archetype_component_count += sparse_set_components.len(); + let sparse_set_archetype_components = (sparse_start..*archetype_component_count).map(ArchetypeComponentId); + archetypes.push(Archetype::new( id, table_id, @@ -712,29 +721,965 @@ impl Archetypes { .into_iter() .zip(sparse_set_archetype_components), )); + id }) } + /// Returns the number of [`ArchetypeComponentId`]s present in the world. #[inline] pub fn archetype_components_len(&self) -> usize { self.archetype_component_count } - /// Clears all entities from all archetypes. + /// Remove all entities from all archetypes. pub(crate) fn clear_entities(&mut self) { for archetype in &mut self.archetypes { archetype.clear_entities(); } } -} -impl Index for Archetypes { - type Output = Archetype; + /// Moves an entity from one archetype to another and returns its new location. + /// + /// # Safety + /// + /// - The `location` given must be the true location of `entity`. + /// - If `ACTION` is [`Insert`](MoveAction::Insert): + /// - The new archetype must have a superset of the components present in `entity`'s current + /// archetype. + /// - The caller must ensure the new table row is written with valid data immediately. + /// - If `ACTION` is [`Take`](MoveAction::Take): + /// - The caller assumes responsibility for dropping the components not present in the new + /// archetype. + /// - If `ACTION` is [`Remove`](MoveAction::Remove): + /// - Nothing else to uphold. + pub(crate) unsafe fn move_entity( + &mut self, + entity: Entity, + location: EntityLocation, + new_archetype_id: ArchetypeId, + entities: &mut Entities, + storages: &mut Storages, + ) -> EntityLocation { + if location.archetype_id == new_archetype_id { + return location; + } - #[inline] - fn index(&self, index: ArchetypeId) -> &Self::Output { - &self.archetypes[index.index()] + let [old_archetype, new_archetype] = self + .get_many_mut([location.archetype_id, new_archetype_id]) + .unwrap(); + + let old_table_id = old_archetype.table_id(); + let new_table_id = new_archetype.table_id(); + + if new_table_id == old_table_id { + move_entity_diff_archetype_same_table( + entity, + location, + entities, + old_archetype, + new_archetype, + ) + } else { + let (old_table, new_table) = storages.tables.get_2_mut(old_table_id, new_table_id); + move_entity_diff_archetype_diff_table::( + entity, + location, + entities, + old_archetype, + new_archetype, + self.archetypes.as_mut_ptr(), + old_table, + new_table, + ) + } + } +} + +/// Used to configure [`move_entity`](Archetypes::move_entity). +pub(crate) enum MoveAction { + /// Component data will be inserted. + Insert, + /// Component data will be removed and dropped. + Remove, + /// Component data will be removed and forgotten. + Take, +} + +/// # Safety +/// +/// +pub(crate) unsafe fn move_entity_diff_archetype_same_table( + entity: Entity, + location: EntityLocation, + entities: &mut Entities, + old_archetype: &mut Archetype, + new_archetype: &mut Archetype, +) -> EntityLocation { + debug_assert_ne!(old_archetype.id, new_archetype.id); + debug_assert_eq!(old_archetype.table_id, new_archetype.table_id); + + // remove `entity` from its old archetype + let result = old_archetype.swap_remove(location.archetype_row); + + // if removing this entity from the archetype causes another entity to move, + // we have to update that entity's archetype row + if let Some(swapped_entity) = result.swapped_entity { + let mut entity_location = entities.get(swapped_entity).unwrap(); + entity_location.archetype_row = location.archetype_row; + // SAFETY: only the archetype row has changed (to row where `entity` was) + unsafe { + entities.set(swapped_entity.index(), entity_location); + } + } + + let new_location = unsafe { new_archetype.allocate(entity, result.table_row) }; + + new_location +} + +/// # Safety +/// +/// +pub(crate) unsafe fn move_entity_diff_archetype_diff_table( + entity: Entity, + location: EntityLocation, + entities: &mut Entities, + old_archetype: &mut Archetype, + new_archetype: &mut Archetype, + archetype_vec_ptr: *mut Archetype, + old_table: &mut Table, + new_table: &mut Table, +) -> EntityLocation { + debug_assert_ne!(old_archetype.id, new_archetype.id); + debug_assert_ne!(old_archetype.table_id, new_archetype.table_id); + + // remove `entity` from its old archetype + let archetype_move = old_archetype.swap_remove(location.archetype_row); + + // if removing this entity from the archetype causes another entity to move, + // we have to update that entity's archetype row + if let Some(swapped_entity) = archetype_move.swapped_entity { + let mut entity_location = entities.get(swapped_entity).unwrap(); + entity_location.archetype_row = location.archetype_row; + // SAFETY: only the archetype row has changed (to row where `entity` was) + unsafe { + entities.set(swapped_entity.index(), entity_location); + } + } + + // move `entity` from the old table to the new table + // SAFETY: `archetype_move.table_row` is valid + let table_move = unsafe { + match ACTION { + MoveAction::Insert => { + old_table.move_to_superset_unchecked(archetype_move.table_row, new_table) + } + MoveAction::Remove => { + old_table.move_to_and_drop_missing_unchecked(archetype_move.table_row, new_table) + } + MoveAction::Take => { + // (caller responsible for dropping the forgetten component values) + old_table.move_to_and_forget_missing_unchecked(archetype_move.table_row, new_table) + } + } + }; + + // SAFETY: `table_move.table_row` is valid + let new_location = unsafe { new_archetype.allocate(entity, table_move.table_row) }; + + // if removing this entity from the table causes another entity to move, + // we have to update that entity's table row + if let Some(swapped_entity) = table_move.swapped_entity { + let mut entity_location = entities.get(swapped_entity).unwrap(); + entity_location.table_row = location.table_row; + // SAFETY: only the table row has changed (to row where `entity` was) + unsafe { + entities.set(swapped_entity.index(), entity_location); + } + + // SAFETY: + // - Dropped the borrow on the other two archetypes. + // - Caller promises there's no borrow on `Archetypes` itself. + let entity_archetype = + unsafe { &mut *archetype_vec_ptr.add(entity_location.archetype_id.index()) }; + + entity_archetype + .set_entity_table_row(entity_location.archetype_row, entity_location.table_row); + } + + // SAFETY: `new_location` is legitimately where `entity`'s data lives + unsafe { + entities.set(entity.index(), new_location); + } + + new_location +} + +impl Archetypes { + /// Deletes the archetype. + pub(crate) fn destroy_archetype( + &mut self, + archetype_id: ArchetypeId, + _components: &Components, + _storages: &mut Storages, + ) { + if let Some(archetype) = self.archetypes.get_mut(archetype_id.index()) { + // TODO: despawn any entities + assert!(archetype.is_empty()); + + // remove archetype from component index + for component_id in archetype.components() { + if let Some(component_archetypes) = self.component_records.get_mut(&component_id) { + component_archetypes.remove(&archetype_id); + } + } + + // remove archetype from the graph + self.graph.remove_node(archetype_id); + } + } + + /// Deletes the component along with all archetypes that have it. + pub(crate) fn destroy_component( + &mut self, + component_id: ComponentId, + components: &Components, + storages: &mut Storages, + ) { + // TODO: use commands here to get batching + if let Some(component_archetypes) = self.component_records.remove(&component_id) { + for (&old_archetype_id, info) in component_archetypes.iter_mut() { + // Don't cache these edges (the nodes are being deleted anyway). + let (new_archetype_id, _) = self.remove_component_from_archetype( + component_id, + old_archetype_id, + components, + storages, + false, + ); + + let [old_archetype, new_archetype] = self + .get_many_mut([old_archetype_id, new_archetype_id]) + .unwrap(); + + let num_entities = old_archetype.len(); + new_archetype.reserve(num_entities); + + if old_archetype.table_id == new_archetype.table_id { + for entry in old_archetype.entities() { + // SAFETY: Enough space exists from the call to `reserve`. + unsafe { + new_archetype.allocate(entry.entity, entry.table_row); + } + } + } else { + let (old_table, new_table) = storages + .tables + .get_2_mut(old_archetype.table_id, new_archetype.table_id); + + new_table.reserve(num_entities); + for entry in old_archetype.entities() { + // SAFETY: Enough space exists from the call to `reserve`. + unsafe { + let new_table_row = + new_table.copy_from_unchecked(old_table, entry.table_row); + new_archetype.allocate(entry.entity, new_table_row); + } + } + } + + // TODO: ArchetypeDeleted + self.destroy_archetype(old_archetype_id, components, storages); + } + } + + if let Some(component_info) = components.get_info(component_id) { + match component_info.storage_type() { + StorageType::Table => { + // TODO: TableDeleted + // destroy tables + } + StorageType::SparseSet => { + // TODO: SparseSetDeleted + // destroy sparse set + } + } + } + } + + /// Returns the [`ArchetypeId`] of the new [`Archetype`] that would be reached by adding the + /// [`Component`](crate::component::Component) to the given [`Archetype`], and a [`bool`] that + /// is `true` if the given [`Archetype`] did *not* already have the [`Component`]. + pub(crate) fn insert_component_into_archetype( + &mut self, + component_id: ComponentId, + archetype_id: ArchetypeId, + components: &Components, + storages: &mut Storages, + cache_edge: bool, + ) -> (ArchetypeId, bool) { + // check cache + if let Some(edge) = self.graph.get_edge_for_insert(archetype_id, component_id) { + let new_archetype_id = edge.dst; + return new_archetype_id; + } + + // SAFETY: component has been initialized + let component_info = unsafe { components.get_info_unchecked(component_id) }; + let component_storage = component_info.storage_type(); + + let current_archetype = self.get(archetype_id).unwrap(); + let (new_archetype_id, added) = if current_archetype.contains(component_id) { + // adding this component does not change the archetype + (archetype_id, false) + } else { + // adding this component changes the archetype + let mut new_table_components = current_archetype.table_components().collect::>(); + let mut new_sparse_set_components = current_archetype + .sparse_set_components() + .collect::>(); + + let new_table_id = match component_storage { + StorageType::Table => { + new_table_components.push(component_id); + new_table_components.sort(); + + // SAFETY: components have all been initialized + unsafe { + storages + .tables + .get_id_or_insert(&new_table_components, components) + } + } + StorageType::SparseSet => { + new_sparse_set_components.push(component_id); + new_sparse_set_components.sort(); + current_archetype.table_id() + } + }; + + ( + self.get_id_or_insert( + new_table_id, + new_table_components, + new_sparse_set_components, + ), + true, + ) + }; + + if cache_edge { + // remember this traversal + self.graph.insert_edge( + archetype_id, + new_archetype_id, + component_id, + ArchetypeEdgeAction::Insert(inserted), + ); + } + + (new_archetype_id, added) + } + + /// Returns the [`ArchetypeId`] of the new [`Archetype`] that would be reached by removing the + /// [`Component`] from the given [`Archetype`], and a [`bool`] that is `true` if the given + /// [`Archetype`] had the [`Component`]. + /// + /// [`Component`]: crate::component::Component + pub(crate) fn remove_component_from_archetype( + &mut self, + component_id: ComponentId, + archetype_id: ArchetypeId, + components: &Components, + storages: &mut Storages, + cache_edge: bool, + ) -> (ArchetypeId, bool) { + // check cache + if let Some(edge) = self.graph.get_edge_for_remove(archetype_id, component_id) { + let new_archetype_id = edge.dst; + let ArchetypeEdgeAction::Remove(removed) = edge.action; + return (new_archetype_id, removed); + } + + // SAFETY: component has been initialized + let component_info = unsafe { components.get_info_unchecked(component_id) }; + let component_storage = component_info.storage_type(); + + let current_archetype = self.get(archetype_id).unwrap(); + let (new_archetype_id, removed) = if current_archetype.contains(component_id) { + // removing this component changes the archetype + let mut new_table_components = current_archetype.table_components().collect::>(); + let mut new_sparse_set_components = current_archetype + .sparse_set_components() + .collect::>(); + + let new_table_id = match component_storage { + StorageType::Table => { + // table component IDs already sorted + let index = new_table_components + .iter() + .position(|&id| id == component_id) + .unwrap(); + new_table_components.remove(index); + + // SAFETY: components have all been initialized + unsafe { + storages + .tables + .get_id_or_insert(&new_table_components, components) + } + } + StorageType::SparseSet => { + // sparse set component IDs already sorted + let index = new_sparse_set_components + .iter() + .position(|&id| id == component_id) + .unwrap(); + new_sparse_set_components.remove(index); + + current_archetype.table_id() + } + }; + + ( + self.get_id_or_insert( + new_table_id, + new_table_components, + new_sparse_set_components, + ), + true, + ) + } else { + // removing this component does not change the archetype + (archetype_id, false) + }; + + if cache_edge { + // remember this traversal + self.graph.insert_edge( + archetype_id, + new_archetype_id, + component_id, + ArchetypeEdgeAction::Remove(removed), + ); + } + + (new_archetype_id, removed) + } + + /// Returns the [`ArchetypeId`] of the new [`Archetype`] that would be reached by adding the + /// [`Bundle`] to the given [`Archetype`]. + /// + /// [`Bundle`]: crate::bundle::Bundle + pub(crate) fn insert_bundle_into_archetype( + &mut self, + bundle: &[ComponentId], + archetype_id: ArchetypeId, + components: &Components, + storages: &mut Storages, + cache_edges: bool, + ) -> ArchetypeId { + // TODO: cache bundle edge (need enum arg, need ComponentId -> Bundle index) + let mut new_archetype_id = archetype_id; + for component_id in bundle.iter().cloned() { + let (next, _) = self.insert_component_into_archetype( + component_id, + new_archetype_id, + components, + storages, + cache_edges, + ); + + new_archetype_id = next; + } + + new_archetype_id + } + + /// Returns the [`ArchetypeId`] of the new [`Archetype`] that would be reached by removing the + /// [`Bundle`] from the given [`Archetype`], and a [`bool`] that is `true` if the given + /// [`Archetype`] had all components in the [`Bundle`]. + /// + /// [`Bundle`]: crate::bundle::Bundle + pub(crate) fn remove_bundle_from_archetype( + &mut self, + bundle: &[ComponentId], + archetype_id: ArchetypeId, + components: &Components, + storages: &mut Storages, + cache_edges: bool, + ) -> (ArchetypeId, bool) { + // TODO: cache bundle edge (need enum arg, need ComponentId -> Bundle index) + let mut new_archetype_id = archetype_id; + let mut has_all = true; + for component_id in bundle.iter().cloned() { + let (next, has) = self.remove_component_from_archetype( + component_id, + new_archetype_id, + components, + storages, + cache_edges, + ); + + new_archetype_id = next; + has_all &= has; + } + + (new_archetype_id, has_all) + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +struct EventId(u64); + +struct EventDescriptor { + category: EventId, + events: Option>, + target: Option, +} + +enum EventTarget { + Entity(Entity), + Archetype(ArchetypeId), +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +struct ObserverId(u64); + +trait EventObserver { + fn run(&mut self, world: &mut World, event: EventDescriptor); +} + +struct Observer { + observes: Box<[EventId]>, + obj: Box, + many_observed: usize, + last_observed: Option, +} + +impl Observer { + pub fn new(observer: impl EventObserver, events: impl Iterator) -> Self { + Self { + observes: events.collect::>().into_boxed_slice(), + obj: Box::new(observer), + many_observed: 0, + last_observed: None, + } + } +} + +enum EventObservers { + Entity(HashMap), + Component { + owned: HashMap, + inherited: HashMap, + owned_or_inherited: HashMap, + // e.g. observes OnAdd(T) and OnRemove(T) for "(R, $target)($this), T($target)" + // non_this_source: HashMap, + }, + Storage { + on_create: HashMap, + on_delete: HashMap, + on_emptied: HashMap, + on_occupied: HashMap, + }, +} + +struct EventRecord { + observers: EventObservers, +} + +struct EventCategoryRecord { + events: SparseSet, +} + +struct EventRecords { + categories: SparseSet, +} + +fn add_observer(observer: impl EventObserver, events: impl Iterator) { + let obj = Observer::new(observer, events); + + // spawn entity + // add observer component (with index?) + for event in events { + // register the observer + } +} + +struct ObserverDescriptor { + category: Entity, + specific: [Id; N], +} + +fn emit(event: EventDescriptor) { + let category_record = category_records[event.category]; + let event_record = category_record[event.specific]; + let observers = event_record.observers; + + // OnArchetypeCreate + // move entity + // T ctor + // OnAdd + // OnSet + // OnSpawn + + // OnDespawn + // UnSet (Drop/Forget) + // OnRemove + // T dtor + // move entity + // OnArchetypeDelete + + world.deferred(|world| { + for observer in event_record.observers.iter_mut() { + observer.notify(world, event); + } + }); + + // run commands generated by observers in depth-first order + while let Some(command) = fifo.pop() { + command.run(world); + } +} + +struct ComponentRecord { + // TODO: sparse sets + archetypes: HashMap, + empty_archetypes: HashMap, + tables: HashSet, + empty_tables: HashSet, +} + +struct ArchetypeComponentRecord { + id: ArchetypeComponentId, + component_id: ComponentId, + archetype_id: ArchetypeId, + archetype_column: ArchetypeColumn, + archetype_column_count: usize, + table: Option, +} + +struct TableComponentRecord { + table_id: TableId, + table_column: TableColumn, + table_column_count: usize, +} + +/// A unique ID for an [`ArchetypeEdge`]. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] +struct ArchetypeEdgeId(u32); + +impl ArchetypeEdgeId { + #[inline] + pub(crate) const fn new(index: usize) -> Self { + Self(index as u32) + } + + #[inline] + pub(crate) fn index(self) -> usize { + self.0 as usize + } +} + +enum ArchetypeEdgeKey { + /// This edge inserts or removes a component. + Component(ComponentId), + /// This edge inserts or removes a component bundle. This is a shortcut we can take with + /// compile-time knowledge. + Bundle(BundleId), +} + +/// Metadata that indicates whether an edge inserts or removes components. +enum ArchetypeEdgeAction { + /// A component will be inserted. + /// + /// The [`bool`] is `true` if the component was not present in the source archetype. + Insert(bool), + /// A component will be removed. + /// + /// The [`bool`] is `true` if the component is present in the source archetype. + Remove(bool), + /// A bundle will be inserted. + /// + /// An element is `true` if the component was not present in the source archetype. + InsertBundle(FixedBitSet), + /// A bundle will be removed. + /// + /// An element is `true` if the component is present in the source archetype. + RemoveBundle(FixedBitSet), +} + +/// The details of a previous move from one [`Archetype`] to another. We save this "manifest" to +/// minimize the cost of doing the same move again later. +pub(crate) struct ArchetypeEdge { + /// The source archetype. + src: ArchetypeId, + /// The destination archetype. + dst: ArchetypeId, + /// The ID of this edge. + id: ArchetypeEdgeId, + /// The ID of the component that was added or removed by this edge. + component_id: ComponentId, + /// Whether the bundle was added or removed. + action: ArchetypeEdgeAction, + /// The previous incoming edge in the destination archetype's linked list. + prev_incoming: Option, + /// The next incoming edge in the destination archetype's linked list. + next_incoming: Option, +} + +struct ArchetypeNode { + id: ArchetypeId, + table_id: TableId, + // TODO: add has_despawn_policy flag + entities: Vec, + components: ImmutableSparseSet, + edges: ArchetypeEdges, + entities_with_despawn_policy: usize, +} + +/// All known edges connecting an [`Archetype`] to others. +/// +/// Adding or removing a [`Component`](crate::component::Component) will move an [`Entity`] from +/// one [`Archetype`] to another. [`ArchetypeEdges`] caches the results of these moves. +/// +/// **Note:** This structure only caches moves that have actually happened (and are still possible +/// to do). A method returning `None` does not mean that inserting or removing a component is +/// invalid, only that such insertion or removal has not happened yet. +#[derive(Default)] +pub(crate) struct ArchetypeEdges { + /// Set of outgoing edges that insert components. + outgoing_insert: HashSet, + /// Set of outgoing edges that remove components. + outgoing_remove: HashSet, + /// Lookup edge that inserts component. + outgoing_insert_index: HashMap, + /// Lookup edge that removes component. + outgoing_remove_index: HashMap, + /// Linked list of incoming edges that insert components. + incoming_insert: Option, + /// Linked list of incoming edges that remove components. + incoming_remove: Option, +} + +impl ArchetypeEdges { + fn get_edge_for_insert(&self, component: ComponentId) -> Option { + self.outgoing_insert_index.get(&component).cloned() + } + + fn get_edge_for_remove(&self, component: ComponentId) -> Option { + self.outgoing_remove_index.get(&component).cloned() + } +} + +#[derive(Default)] +struct ArchetypeGraph { + nodes: Arena, + edges: Arena, +} + +impl ArchetypeGraph { + fn node_count(&self) -> usize { + self.nodes.len() + } + + fn edge_count(&self) -> usize { + self.edges.len() + } + + fn insert_node(&mut self, id: ArchetypeId) { + // TODO: copy get_or_insert_id + todo!() + } + + fn insert_edge( + &mut self, + src: ArchetypeId, + dst: ArchetypeId, + component_id: ComponentId, + action: ArchetypeEdgeAction, + ) { + let insert = matches!(action, ArchetypeEdgeAction::Insert(_)); + // + let edge_id: ArchetypeEdgeId; + let mut new_edge = ArchetypeEdge { + src, + dst, + id: edge_id, + component_id, + action, + prev_incoming: None, + next_incoming: None, + }; + + // add to the source archetype's set of outgoing edges + let (mut src_archetype, _) = self.nodes.get_unknown_gen_mut(src.index()).unwrap(); + if insert { + src_archetype.edges.outgoing_insert.insert(new_edge.id); + src_archetype + .edges + .outgoing_insert_index + .insert(component_id, new_edge.id); + } else { + src_archetype.edges.outgoing_remove.insert(new_edge.id); + src_archetype + .edges + .outgoing_remove_index + .insert(component_id, new_edge.id); + } + + // add to the destination archetype's linked list of incoming edges + let (mut dst_archetype, _) = self.nodes.get_unknown_gen_mut(dst.index()).unwrap(); + let head = if insert { + &mut dst_archetype.edges.incoming_insert + } else { + &mut dst_archetype.edges.incoming_remove + }; + + new_edge.next_incoming = *head; + if let Some(edge_id) = head { + let head_edge = self.edges.get_mut(edge_id).unwrap(); + debug_assert!(head_edge.prev_incoming.is_none()); + head_edge.prev_incoming = Some(new_edge.id); + } + *head = Some(new_edge.id); + + self.edges.insert(new_edge); + } + + fn remove_node(&mut self, node_id: ArchetypeId) { + remove_node_from_graph(node_id, &mut self.nodes, &mut self.edges); + } + + fn remove_edge(&mut self, edge_id: ArchetypeEdgeId) { + remove_edge_from_graph(edge_id, &mut self.nodes, &mut self.edges); + } + + /// Returns a reference to the [`ArchetypeEdge`] that was cached after adding + /// the `component` to the ones in the `archetype`. + fn get_edge_for_insert( + &self, + archetype_id: ArchetypeId, + component_id: ComponentId, + ) -> Option<&ArchetypeEdge> { + if let Some((node, _)) = self.nodes.get_unknown_gen(archetype_id.index()) { + if let Some(edge_id) = node.edges.get_edge_for_insert(component_id) { + return self.edges.get(edge_id); + } + } + + None + } + + /// Returns a reference to the [`ArchetypeEdge`] that was cached after removing + /// the `component` from the ones in the `archetype`. + fn get_edge_for_remove( + &self, + archetype_id: ArchetypeId, + component_id: ComponentId, + ) -> Option<&ArchetypeEdge> { + if let Some(node) = self.nodes.get(archetype_id) { + if let Some(edge_id) = node.edges.get_edge_for_remove(component_id) { + return self.edges.get(edge_id); + } + } + + None + } +} + +fn remove_node_from_graph( + node_id: ArchetypeId, + nodes: &mut Arena, + edges: &mut Arena, +) -> ArchetypeNode { + // remove the node from the graph + let src_archetype = nodes.remove(node_id).unwrap(); + + // remove its incoming insert edges + let mut next = src_archetype.edges.incoming_insert; + while let Some(edge_id) = next { + let edge = remove_edge_from_graph(edge_id, nodes, edges); + next = edge.next_incoming; + } + + // remove its incoming remove edges + let mut next = src_archetype.edges.incoming_remove; + while let Some(edge_id) = next { + let edge = remove_edge_from_graph(edge_id, nodes, edges); + next = edge.next_incoming; + } + + // remove its outgoing insert edges + for edge_id in src_archetype.edges.outgoing_insert.into_iter() { + remove_edge_from_graph(edge_id, nodes, edges); + } + + // remove its outgoing remove edges + for edge_id in src_archetype.edges.outgoing_remove.into_iter() { + remove_edge_from_graph(edge_id, nodes, edges); + } + + src_archetype +} + +fn remove_edge_from_graph( + edge_id: ArchetypeEdgeId, + nodes: &mut Arena, + edges: &mut Arena, +) -> ArchetypeEdge { + // remove the edge from the graph + let edge = edges.remove(edge_id).unwrap(); + + // repair the linked list it was in + if let Some(n) = edge.next_incoming { + let next = edges.get_mut(n).unwrap(); + next.prev_incoming = edge.prev_incoming; + } + + if let Some(p) = edge.prev_incoming { + let prev = edges.get_mut(p).unwrap(); + prev.next_incoming = edge.next_incoming; + } else { + // edge was head of the list + // make destination node forget about it + if let Some(dst) = nodes.get_mut(edge.dst) { + match edge.action { + ArchetypeEdgeAction::Insert(_) => { + dst.edges.incoming_insert = edge.next_incoming; + } + ArchetypeEdgeAction::Remove(_) => { + dst.edges.incoming_remove = edge.next_incoming; + } + } + } + } + + // if source is not being removed, remove this edge from it + if let Some(src) = nodes.get_mut(edge.src.index()) { + match edge.action { + ArchetypeEdgeAction::Insert(_) => { + src.edges.outgoing_insert.remove(&edge.id); + src.edges.outgoing_insert_index.remove(&edge.component_id); + } + ArchetypeEdgeAction::Remove(_) => { + src.edges.outgoing_remove.remove(&edge.id); + src.edges.outgoing_remove_index.remove(&edge.component_id); + } + } + } + + edge +} + +impl Index for Archetypes { + type Output = Archetype; + + #[inline] + fn index(&self, index: ArchetypeId) -> &Self::Output { + &self.archetypes[index.index()] } } @@ -744,3 +1689,100 @@ impl IndexMut for Archetypes { &mut self.archetypes[index.index()] } } + +/// Returns `true` if all indices are different and less than `len`. +fn get_many_check_valid(indices: &[usize; N], len: usize) -> bool { + // (from `std` impl) + // NOTE: The compiler should unroll these loops into a sequence + // of instructions without additional branching. + let mut valid = true; + for (i, &idx) in indices.iter().enumerate() { + valid &= idx < len; + for &idx2 in &indices[..i] { + valid &= idx != idx2; + } + } + valid +} + +/// Containers that can return an array of unique mutable references to disjoint elements. +trait GetManyMut { + // # Safety + unsafe fn get_many_unchecked_mut(&mut self, indices: [usize; N]) + -> [&mut T; N]; + fn get_many_mut(&mut self, indices: [usize; N]) -> Option<[&mut T; N]>; +} + +impl GetManyMut for [T] { + unsafe fn get_many_unchecked_mut( + &mut self, + indices: [usize; N], + ) -> [&mut T; N] { + // (from `std` impl) + // NOTE: This implementation looks like this because any variation of + // `indices.map(|i| self.get_unchecked_mut(i))` makes miri complain, + // or compiles to worse code. + let slice: *mut [T] = self; + let mut array: core::mem::MaybeUninit<[&mut T; N]> = core::mem::MaybeUninit::uninit(); + let array_ptr = array.as_mut_ptr(); + // SAFETY: no duplicate or out-of-bounds indices + unsafe { + for i in 0..N { + let idx = *indices.get_unchecked(i); + *(*array_ptr).get_unchecked_mut(i) = &mut *(*slice).get_unchecked_mut(idx); + } + + array.assume_init() + } + } + + fn get_many_mut(&mut self, indices: [usize; N]) -> Option<[&mut T; N]> { + if !get_many_check_valid(&indices, self.len()) { + // duplicate or out-of-bounds indices + return None; + } + + // SAFETY: The `get_many_check_valid()` call checked that all indices + // are disjunct and in bounds. + unsafe { Some(self.get_many_unchecked_mut(indices)) } + } +} + +fn sorted_remove(source: &mut Vec, remove: &[T]) { + let mut i = 0; + source.retain(|&value| { + while i < remove.len() && value > remove[i] { + i += 1; + } + + if i < remove.len() { + value != remove[i] + } else { + true + } + }); +} + +#[cfg(test)] +mod tests { + #[test] + fn sorted_remove() { + let mut a = vec![1, 2, 3, 4, 5, 6, 7]; + let b = vec![1, 2, 3, 5, 7]; + super::sorted_remove(&mut a, &b); + + assert_eq!(a, vec![4, 6]); + + let mut a = vec![1]; + let b = vec![1]; + super::sorted_remove(&mut a, &b); + + assert_eq!(a, vec![]); + + let mut a = vec![1]; + let b = vec![2]; + super::sorted_remove(&mut a, &b); + + assert_eq!(a, vec![1]); + } +} diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 162fe07c0beda..0c801c3f79e25 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -5,13 +5,14 @@ pub use bevy_ecs_macros::Bundle; use bevy_utils::HashSet; +use crate::archetype::{ + move_entity_diff_archetype_diff_table, move_entity_diff_archetype_same_table, +}; use crate::{ - archetype::{ - Archetype, ArchetypeId, Archetypes, BundleComponentStatus, ComponentStatus, - SpawnBundleStatus, - }, + archetype::{Archetype, ArchetypeId, Archetypes}, component::{Component, ComponentId, ComponentStorage, Components, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, + removal_detection::RemovedComponentEvents, storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, TypeIdMap, }; @@ -128,109 +129,107 @@ use std::any::TypeId; /// /// # Safety /// -/// Manual implementations of this trait are unsupported. +/// Manual implementation of this trait is not supported. /// That is, there is no safe way to implement this trait, and you must not do so. -/// If you want a type to implement [`Bundle`], you must use [`derive@Bundle`](derive@Bundle). +/// If you want a type to implement [`Bundle`], you must use [`#[derive(Bundle)]`](derive@Bundle). /// /// [`Query`]: crate::system::Query // Some safety points: // - [`Bundle::component_ids`] must return the [`ComponentId`] for each component type in the -// bundle, in the _exact_ order that [`Bundle::get_components`] is called. -// - [`Bundle::from_components`] must call `func` exactly once for each [`ComponentId`] returned by +// bundle, in the _exact_ order that [`Bundle::map_components`] is called. +// - [`Bundle::read_components`] must call `func` exactly once for each [`ComponentId`] returned by // [`Bundle::component_ids`]. pub unsafe trait Bundle: Send + Sync + 'static { - /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s + /// Calls `f` with the [`ComponentId`] of each component, in the order they appear + /// in the [`Bundle`]. #[doc(hidden)] fn component_ids( components: &mut Components, storages: &mut Storages, - ids: &mut impl FnMut(ComponentId), + f: &mut impl FnMut(ComponentId), ); - /// Calls `func`, which should return data for each component in the bundle, in the order of - /// this bundle's [`Component`]s + /// Calls `f`, which reads the data of each component in the bundle, in the order they appear + /// in the [`Bundle`]. /// /// # Safety - /// Caller must return data for each component in the bundle, in the order of this bundle's - /// [`Component`]s + /// + /// The caller must iterate components in the order returned by [`component_ids`](Bundle::component_ids). #[doc(hidden)] - unsafe fn from_components(ctx: &mut T, func: &mut F) -> Self + unsafe fn read_components(context: &mut T, f: &mut F) -> Self where - // Ensure that the `OwningPtr` is used correctly F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>, Self: Sized; - // SAFETY: - // The `StorageType` argument passed into [`Bundle::get_components`] must be correct for the - // component being fetched. - // - /// Calls `func` on each value, in the order of this bundle's [`Component`]s. This passes - /// ownership of the component values to `func`. + /// Calls `f` on each component value, in the order they appear in the [`Bundle`]. + /// + /// # Safety + /// + /// The caller must: + /// - Iterate components in the order returned by [`component_ids`](Bundle::component_ids). + /// - Supply the correct [`StorageType`] value for each component. #[doc(hidden)] - fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)); + unsafe fn map_components(self, f: &mut impl FnMut(StorageType, OwningPtr<'_>)); } // SAFETY: -// - `Bundle::component_ids` calls `ids` for C's component id (and nothing else) -// - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on it's associated constant. -// - `Bundle::from_components` calls `func` exactly once for C, which is the exact value returned by `Bundle::component_ids`. +// - `Bundle::component_ids` returns the `ComponentId` of `C`. +// - `Bundle::read_components` calls `f` once the ID returned by `Bundle::component_ids`. +// - `Bundle::map_components` calls `f` once with the value returned by `Bundle::read_components`. unsafe impl Bundle for C { fn component_ids( components: &mut Components, storages: &mut Storages, - ids: &mut impl FnMut(ComponentId), + f: &mut impl FnMut(ComponentId), ) { - ids(components.init_component::(storages)); + f(components.init_component::(storages)); } - unsafe fn from_components(ctx: &mut T, func: &mut F) -> Self + unsafe fn read_components(context: &mut T, f: &mut F) -> Self where - // Ensure that the `OwningPtr` is used correctly F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>, Self: Sized, { - // Safety: The id given in `component_ids` is for `Self` - func(ctx).read() + // SAFETY: The id given in `component_ids` is for `Self` + f(context).read() } #[inline] - fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) { - OwningPtr::make(self, |ptr| func(C::Storage::STORAGE_TYPE, ptr)); + unsafe fn map_components(self, f: &mut impl FnMut(StorageType, OwningPtr<'_>)) { + OwningPtr::make(self, |ptr| f(C::Storage::STORAGE_TYPE, ptr)); } } macro_rules! tuple_impl { ($($name: ident),*) => { // SAFETY: - // - `Bundle::component_ids` calls `ids` for each component type in the - // bundle, in the exact order that `Bundle::get_components` is called. - // - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`. - // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct - // `StorageType` into the callback. + // - `Bundle::component_ids` returns `ComponentId` in the order the types appear in the tuple. + // - `Bundle::read_components` calls `f` once with each ID returned by `Bundle::component_ids`. + // - `Bundle::map_components` calls `f` once with each value returned by `Bundle::read_components`. unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) { #[allow(unused_variables)] - fn component_ids(components: &mut Components, storages: &mut Storages, ids: &mut impl FnMut(ComponentId)){ - $(<$name as Bundle>::component_ids(components, storages, ids);)* + fn component_ids(components: &mut Components, storages: &mut Storages, f: &mut impl FnMut(ComponentId)){ + $(<$name as Bundle>::component_ids(components, storages, f);)* } #[allow(unused_variables, unused_mut)] #[allow(clippy::unused_unit)] - unsafe fn from_components(ctx: &mut T, func: &mut F) -> Self + unsafe fn read_components(context: &mut T, f: &mut F) -> Self where F: FnMut(&mut T) -> OwningPtr<'_> { // Rust guarantees that tuple calls are evaluated 'left to right'. // https://doc.rust-lang.org/reference/expressions.html#evaluation-order-of-operands - ($(<$name as Bundle>::from_components(ctx, func),)*) + ($(<$name as Bundle>::read_components(context, f),)*) } #[allow(unused_variables, unused_mut)] #[inline(always)] - fn get_components(self, func: &mut impl FnMut(StorageType, OwningPtr<'_>)) { + unsafe fn map_components(self, f: &mut impl FnMut(StorageType, OwningPtr<'_>)) { #[allow(non_snake_case)] let ($(mut $name,)*) = self; $( - $name.get_components(&mut *func); + $name.map_components(&mut *f); )* } } @@ -239,6 +238,11 @@ macro_rules! tuple_impl { all_tuples!(tuple_impl, 0, 15, B); +/// An opaque, unique key representing a [`Bundle`]. +/// +/// **Note:** `BundleId` values are not globally unique and should only be considered +/// valid in their original world. Assume that a `BundleId` from one world will never +/// refer to the same bundle in any other world. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct BundleId(usize); @@ -260,9 +264,11 @@ impl SparseSetIndex for BundleId { } } +/// The [`BundleId`] of the [`Bundle`] and the [`ComponentId`] of each [`Component`] in it. +#[derive(Clone)] pub struct BundleInfo { pub(crate) id: BundleId, - pub(crate) component_ids: Vec, + pub(crate) component_ids: Box<[ComponentId]>, } impl BundleInfo { @@ -276,78 +282,78 @@ impl BundleInfo { &self.component_ids } - pub(crate) fn get_bundle_inserter<'a, 'b>( - &'b self, - entities: &'a mut Entities, - archetypes: &'a mut Archetypes, + /// Returns a [`BundleInserter`] that can add the components in the [`Bundle`] to + /// entities in the archetype given by the [`ArchetypeId`]. + pub(crate) fn make_bundle_inserter<'bundle, 'world>( + &'bundle self, + entities: &'world mut Entities, + archetypes: &'world mut Archetypes, components: &mut Components, - storages: &'a mut Storages, + storages: &'world mut Storages, archetype_id: ArchetypeId, change_tick: u32, - ) -> BundleInserter<'a, 'b> { - let new_archetype_id = - self.add_bundle_to_archetype(archetypes, storages, components, archetype_id); - let archetypes_ptr = archetypes.archetypes.as_mut_ptr(); - if new_archetype_id == archetype_id { - let archetype = &mut archetypes[archetype_id]; - let table_id = archetype.table_id(); - BundleInserter { - bundle_info: self, - archetype, - entities, - sparse_sets: &mut storages.sparse_sets, - table: &mut storages.tables[table_id], - archetypes_ptr, - change_tick, - result: InsertBundleResult::SameArchetype, - } + ) -> BundleInserter<'bundle, 'world> { + let new_archetype_id = archetypes.insert_bundle_into_archetype( + &self.component_ids, + archetype_id, + components, + storages, + true, + ); + + let archetype_vec_ptr = archetypes.archetypes.as_mut_ptr(); + + let (archetype, new_archetype) = archetypes.get_2_mut(archetype_id, new_archetype_id); + let table_id = archetype.table_id(); + let new_table_id = new_archetype.table_id(); + let (table, new_table) = storages.tables.get_2_mut(table_id, new_table_id); + + let result = if archetype_id == new_archetype_id { + MoveResult::SameArchetype } else { - let (archetype, new_archetype) = archetypes.get_2_mut(archetype_id, new_archetype_id); - let table_id = archetype.table_id(); - if table_id == new_archetype.table_id() { - BundleInserter { - bundle_info: self, - archetype, - archetypes_ptr, - entities, - sparse_sets: &mut storages.sparse_sets, - table: &mut storages.tables[table_id], - change_tick, - result: InsertBundleResult::NewArchetypeSameTable { new_archetype }, - } + if table_id == new_table_id { + MoveResult::NewArchetypeSameTable { new_archetype } } else { - let (table, new_table) = storages - .tables - .get_2_mut(table_id, new_archetype.table_id()); - BundleInserter { - bundle_info: self, - archetype, - sparse_sets: &mut storages.sparse_sets, - entities, - archetypes_ptr, - table, - change_tick, - result: InsertBundleResult::NewArchetypeNewTable { - new_archetype, - new_table, - }, + MoveResult::NewArchetypeNewTable { + new_archetype, + new_table, + archetype_vec_ptr, } } + }; + + BundleInserter { + archetype, + bundle_info: self, + table, + entities, + sparse_sets: &mut storages.sparse_sets, + change_tick, + result, } } - pub(crate) fn get_bundle_spawner<'a, 'b>( - &'b self, - entities: &'a mut Entities, - archetypes: &'a mut Archetypes, + /// Returns a [`BundleSpawner`] that can add the components in the [`Bundle`] to + /// entities in the archetype. + pub(crate) fn make_bundle_spawner<'bundle, 'world>( + &'bundle self, + entities: &'world mut Entities, + archetypes: &'world mut Archetypes, components: &mut Components, - storages: &'a mut Storages, + storages: &'world mut Storages, change_tick: u32, - ) -> BundleSpawner<'a, 'b> { - let new_archetype_id = - self.add_bundle_to_archetype(archetypes, storages, components, ArchetypeId::EMPTY); - let archetype = &mut archetypes[new_archetype_id]; - let table = &mut storages.tables[archetype.table_id()]; + ) -> BundleSpawner<'bundle, 'world> { + let new_archetype_id = archetypes.insert_bundle_into_archetype( + &self.component_ids, // + ArchetypeId::EMPTY, + components, + storages, + true, + ); + + let archetype = archetypes.get_mut(new_archetype_id).unwrap(); + let table = storages.tables.get_mut(archetype.table_id()).unwrap(); + BundleSpawner { archetype, bundle_info: self, @@ -358,160 +364,47 @@ impl BundleInfo { } } - /// This writes components from a given [`Bundle`] to the given entity. - /// - /// # Safety - /// - /// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component - /// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added) - /// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status - /// should be `Mutated`. If the original archetype does not have `ComponentA`, the status should be `Added`. - /// When "inserting" a bundle into an existing entity, [`AddBundle`](crate::archetype::AddBundle) - /// should be used, which will report `Added` vs `Mutated` status based on the current archetype's structure. - /// When spawning a bundle, [`SpawnBundleStatus`] can be used instead, which removes the need - /// to look up the [`AddBundle`](crate::archetype::AddBundle) in the archetype graph, which requires - /// ownership of the entity's current archetype. - /// - /// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the - /// `entity`, `bundle` must match this [`BundleInfo`]'s type - #[inline] - #[allow(clippy::too_many_arguments)] - unsafe fn write_components( - &self, - table: &mut Table, - sparse_sets: &mut SparseSets, - bundle_component_status: &S, - entity: Entity, - table_row: TableRow, - change_tick: u32, - bundle: T, - ) { - // NOTE: get_components calls this closure on each component in "bundle order". - // bundle_info.component_ids are also in "bundle order" - let mut bundle_component = 0; - bundle.get_components(&mut |storage_type, component_ptr| { - let component_id = *self.component_ids.get_unchecked(bundle_component); - match storage_type { - StorageType::Table => { - let column = table.get_column_mut(component_id).unwrap(); - // SAFETY: bundle_component is a valid index for this bundle - match bundle_component_status.get_status(bundle_component) { - ComponentStatus::Added => { - column.initialize(table_row, component_ptr, Tick::new(change_tick)); - } - ComponentStatus::Mutated => { - column.replace(table_row, component_ptr, change_tick); - } - } - } - StorageType::SparseSet => { - sparse_sets.get_mut(component_id).unwrap().insert( - entity, - component_ptr, - change_tick, - ); - } - } - bundle_component += 1; - }); - } - - /// Adds a bundle to the given archetype and returns the resulting archetype. This could be the - /// same [`ArchetypeId`], in the event that adding the given bundle does not result in an - /// [`Archetype`] change. Results are cached in the [`Archetype`] graph to avoid redundant work. - pub(crate) fn add_bundle_to_archetype( - &self, - archetypes: &mut Archetypes, - storages: &mut Storages, + pub(crate) fn make_bundle_remover<'bundle, 'world>( + &'bundle self, + entities: &'world mut Entities, + archetypes: &'world mut Archetypes, components: &mut Components, + storages: &'world mut Storages, archetype_id: ArchetypeId, - ) -> ArchetypeId { - if let Some(add_bundle_id) = archetypes[archetype_id].edges().get_add_bundle(self.id) { - return add_bundle_id; - } - let mut new_table_components = Vec::new(); - let mut new_sparse_set_components = Vec::new(); - let mut bundle_status = Vec::with_capacity(self.component_ids.len()); - - let current_archetype = &mut archetypes[archetype_id]; - for component_id in self.component_ids.iter().cloned() { - if current_archetype.contains(component_id) { - bundle_status.push(ComponentStatus::Mutated); - } else { - bundle_status.push(ComponentStatus::Added); - // SAFETY: component_id exists - let component_info = unsafe { components.get_info_unchecked(component_id) }; - match component_info.storage_type() { - StorageType::Table => new_table_components.push(component_id), - StorageType::SparseSet => new_sparse_set_components.push(component_id), - } - } - } + change_tick: u32, + ) { + let new_archetype_id = archetypes.remove_bundle_from_archetype( + &self.component_ids, + archetype_id, + components, + storages, + true, + ); - if new_table_components.is_empty() && new_sparse_set_components.is_empty() { - let edges = current_archetype.edges_mut(); - // the archetype does not change when we add this bundle - edges.insert_add_bundle(self.id, archetype_id, bundle_status); - archetype_id - } else { - let table_id; - let table_components; - let sparse_set_components; - // the archetype changes when we add this bundle. prepare the new archetype and storages - { - let current_archetype = &archetypes[archetype_id]; - table_components = if new_table_components.is_empty() { - // if there are no new table components, we can keep using this table - table_id = current_archetype.table_id(); - current_archetype.table_components().collect() - } else { - new_table_components.extend(current_archetype.table_components()); - // sort to ignore order while hashing - new_table_components.sort(); - // SAFETY: all component ids in `new_table_components` exist - table_id = unsafe { - storages - .tables - .get_id_or_insert(&new_table_components, components) - }; - - new_table_components - }; - - sparse_set_components = if new_sparse_set_components.is_empty() { - current_archetype.sparse_set_components().collect() - } else { - new_sparse_set_components.extend(current_archetype.sparse_set_components()); - // sort to ignore order while hashing - new_sparse_set_components.sort(); - new_sparse_set_components - }; - }; - let new_archetype_id = - archetypes.get_id_or_insert(table_id, table_components, sparse_set_components); - // add an edge from the old archetype to the new archetype - archetypes[archetype_id].edges_mut().insert_add_bundle( - self.id, - new_archetype_id, - bundle_status, - ); - new_archetype_id - } + todo!() } } -pub(crate) struct BundleInserter<'a, 'b> { - pub(crate) archetype: &'a mut Archetype, - pub(crate) entities: &'a mut Entities, - bundle_info: &'b BundleInfo, - table: &'a mut Table, - sparse_sets: &'a mut SparseSets, - result: InsertBundleResult<'a>, - archetypes_ptr: *mut Archetype, +pub(crate) struct BundleInserter<'bundle, 'world> { + bundle_info: &'bundle BundleInfo, + pub(crate) entities: &'world mut Entities, + pub(crate) archetype: &'world mut Archetype, + table: &'world mut Table, + sparse_sets: &'world mut SparseSets, + change_tick: u32, + result: MoveResult<'world>, +} + +pub(crate) struct BundleSpawner<'bundle, 'world> { + bundle_info: &'bundle BundleInfo, + pub(crate) entities: &'world mut Entities, + pub(crate) archetype: &'world mut Archetype, + table: &'world mut Table, + sparse_sets: &'world mut SparseSets, change_tick: u32, } -pub(crate) enum InsertBundleResult<'a> { +pub(crate) enum MoveResult<'a> { SameArchetype, NewArchetypeSameTable { new_archetype: &'a mut Archetype, @@ -519,13 +412,18 @@ pub(crate) enum InsertBundleResult<'a> { NewArchetypeNewTable { new_archetype: &'a mut Archetype, new_table: &'a mut Table, + // Tables can store values from any number of archetypes. When we swap-remove an entity + // from the original table, we'll have to update its archetype. We may need to borrow a + // third archetype, so we store this pointer to the memory of the `Vec`. + archetype_vec_ptr: *mut Archetype, }, } -impl<'a, 'b> BundleInserter<'a, 'b> { +impl BundleInserter<'_, '_> { /// # Safety - /// `entity` must currently exist in the source archetype for this inserter. `archetype_row` - /// must be `entity`'s location in the archetype. `T` must match this [`BundleInfo`]'s type + /// - `entity` must currently exist in the source archetype for this inserter. + /// - `archetype_row` must be `entity`'s location in the archetype. + /// - `T` must match this [`BundleInfo`]'s type #[inline] pub unsafe fn insert( &mut self, @@ -533,122 +431,82 @@ impl<'a, 'b> BundleInserter<'a, 'b> { location: EntityLocation, bundle: T, ) -> EntityLocation { - match &mut self.result { - InsertBundleResult::SameArchetype => { - // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) - let add_bundle = self - .archetype - .edges() - .get_add_bundle_internal(self.bundle_info.id) - .unwrap(); - self.bundle_info.write_components( - self.table, - self.sparse_sets, - add_bundle, - entity, - location.table_row, - self.change_tick, - bundle, - ); - location - } - InsertBundleResult::NewArchetypeSameTable { new_archetype } => { - let result = self.archetype.swap_remove(location.archetype_row); - if let Some(swapped_entity) = result.swapped_entity { - self.entities.set(swapped_entity.index(), location); - } - let new_location = new_archetype.allocate(entity, result.table_row); - self.entities.set(entity.index(), new_location); - - // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) - let add_bundle = self - .archetype - .edges() - .get_add_bundle_internal(self.bundle_info.id) - .unwrap(); - self.bundle_info.write_components( - self.table, - self.sparse_sets, - add_bundle, + // move entity + let (new_location, new_table) = match self.result { + MoveResult::SameArchetype => (location, self.table), + MoveResult::NewArchetypeSameTable { new_archetype } => { + let new_location = move_entity_diff_archetype_same_table( entity, - result.table_row, - self.change_tick, - bundle, + location, + self.entities, + self.archetype, + new_archetype, ); - new_location + + (new_location, self.table) } - InsertBundleResult::NewArchetypeNewTable { + MoveResult::NewArchetypeNewTable { new_archetype, new_table, + archetype_vec_ptr, } => { - let result = self.archetype.swap_remove(location.archetype_row); - if let Some(swapped_entity) = result.swapped_entity { - self.entities.set(swapped_entity.index(), location); - } - // PERF: store "non bundle" components in edge, then just move those to avoid - // redundant copies - let move_result = self - .table - .move_to_superset_unchecked(result.table_row, new_table); - let new_location = new_archetype.allocate(entity, move_result.new_row); - self.entities.set(entity.index(), new_location); - - // if an entity was moved into this entity's table spot, update its table row - if let Some(swapped_entity) = move_result.swapped_entity { - let swapped_location = self.entities.get(swapped_entity).unwrap(); - let swapped_archetype = if self.archetype.id() == swapped_location.archetype_id - { - &mut *self.archetype - } else if new_archetype.id() == swapped_location.archetype_id { - new_archetype - } else { - // SAFETY: the only two borrowed archetypes are above and we just did collision checks - &mut *self - .archetypes_ptr - .add(swapped_location.archetype_id.index()) - }; - - swapped_archetype - .set_entity_table_row(swapped_location.archetype_row, result.table_row); - } - - // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) - let add_bundle = self - .archetype - .edges() - .get_add_bundle_internal(self.bundle_info.id) - .unwrap(); - self.bundle_info.write_components( - new_table, - self.sparse_sets, - add_bundle, + let new_location = move_entity_diff_archetype_diff_table::( entity, - move_result.new_row, - self.change_tick, - bundle, + location, + self.entities, + self.archetype, + new_archetype, + archetype_vec_ptr, + self.table, + new_table, ); - new_location + + (new_location, new_table) } - } + }; + + // write data + self.bundle_info.write_components( + entity, + bundle, + bundle_component_status, + self.change_tick, + new_table, + new_location.table_row, + self.sparse_sets, + ); + + new_location } -} -pub(crate) struct BundleSpawner<'a, 'b> { - pub(crate) archetype: &'a mut Archetype, - pub(crate) entities: &'a mut Entities, - bundle_info: &'b BundleInfo, - table: &'a mut Table, - sparse_sets: &'a mut SparseSets, - change_tick: u32, + /// # Safety + /// + /// + pub unsafe fn remove(&mut self, entity: Entity, location: EntityLocation) { + todo!() + } + + /// # Safety + /// + /// + pub unsafe fn take( + &mut self, + entity: Entity, + location: EntityLocation, + ) -> Option { + todo!() + } } -impl<'a, 'b> BundleSpawner<'a, 'b> { +impl BundleSpawner<'_, '_> { pub fn reserve_storage(&mut self, additional: usize) { self.archetype.reserve(additional); self.table.reserve(additional); } + /// # Safety - /// `entity` must be allocated (but non-existent), `T` must match this [`BundleInfo`]'s type + /// - `entity` must be allocated (with no storage yet) + /// - `T` must be the [`Bundle`] type from the [`BundleInfo`] #[inline] pub unsafe fn spawn_non_existent( &mut self, @@ -658,13 +516,13 @@ impl<'a, 'b> BundleSpawner<'a, 'b> { let table_row = self.table.allocate(entity); let location = self.archetype.allocate(entity, table_row); self.bundle_info.write_components( - self.table, - self.sparse_sets, - &SpawnBundleStatus, entity, - table_row, - self.change_tick, bundle, + &SpawnBundleStatus, + self.change_tick, + self.table, + table_row, + self.sparse_sets, ); self.entities.set(entity.index(), location); @@ -672,16 +530,17 @@ impl<'a, 'b> BundleSpawner<'a, 'b> { } /// # Safety - /// `T` must match this [`BundleInfo`]'s type + /// - `T` must be the [`Bundle`] type from the [`BundleInfo`] #[inline] pub unsafe fn spawn(&mut self, bundle: T) -> Entity { let entity = self.entities.alloc(); - // SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type + // SAFETY: `entity` is allocated (with no storage yet), `BundleInfo` is for `T` self.spawn_non_existent(entity, bundle); entity } } +/// Stores metadata for every known [`Bundle`]. #[derive(Default)] pub struct Bundles { bundle_infos: Vec, @@ -704,15 +563,14 @@ impl Bundles { components: &mut Components, storages: &mut Storages, ) -> &'a BundleInfo { - let bundle_infos = &mut self.bundle_infos; let id = self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { let mut component_ids = Vec::new(); T::component_ids(components, storages, &mut |id| component_ids.push(id)); - let id = BundleId(bundle_infos.len()); + let id = BundleId(self.bundle_infos.len()); let bundle_info = // SAFETY: T::component_id ensures info was created unsafe { initialize_bundle(std::any::type_name::(), components, component_ids, id) }; - bundle_infos.push(bundle_info); + self.bundle_infos.push(bundle_info); id }); // SAFETY: index either exists, or was initialized @@ -720,9 +578,113 @@ impl Bundles { } } +/// Writes components values from a [`Bundle`] to an entity. +/// /// # Safety /// -/// `component_id` must be valid [`ComponentId`]'s +/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component +/// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added) +/// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status +/// should be `Mutated`. If the original archetype does not have `ComponentA`, the status should be `Added`. +/// When "inserting" a bundle into an existing entity, [`AddBundle`](crate::archetype::AddBundle) +/// should be used, which will report `Added` vs `Mutated` status based on the current archetype's structure. +/// +/// When spawning a bundle, [`SpawnBundleStatus`] can be used instead, which removes the need +/// to look up the [`AddBundle`](crate::archetype::AddBundle) in the archetype graph, which requires +/// ownership of the entity's current archetype. +/// +/// - `table` must be the "new" table for `entity`. +/// - `table_row` must have space allocated for the `entity` +/// - `bundle` must match this [`BundleInfo`]'s type +#[inline] +#[allow(clippy::too_many_arguments)] +pub(crate) unsafe fn write_components( + entity: Entity, + bundle: T, + info: &BundleInfo, + added: &[bool], + change_tick: u32, + table: &mut Table, + table_row: TableRow, + sparse_sets: &mut SparseSets, +) { + // NOTE: `self.component_ids` matches the order returned by `T::component_ids` + let mut bundle_component_index = 0; + bundle.map_components(&mut |component_storage_type, component_ptr| { + let component_id = *info.component_ids.get_unchecked(bundle_component_index); + match component_storage_type { + StorageType::Table => { + let column = table.get_column_mut(component_id).unwrap(); + // SAFETY: bundle_component is a valid index for this bundle + if added[bundle_component_index] { + column.initialize(table_row, component_ptr, Tick::new(change_tick)); + } else { + column.replace(table_row, component_ptr, change_tick); + } + } + StorageType::SparseSet => { + sparse_sets.get_mut(component_id).unwrap().insert( + entity, + component_ptr, + change_tick, + ); + } + } + + bundle_component_index += 1; + }); +} + +/// Takes ownership of stored component data. +/// +/// This function leaves the memory unchanged, but the component behind the returned pointer is +/// now owned by the caller, who assumes responsibility for dropping it. The caller must copy the +/// data to a new location and then ensure the original location is forgotten. +/// +/// # Safety +/// +/// - All arguments must come from the same [`World`]. +/// - `component_id` must be valid. +/// - `location.table_row` must be a valid index into the relevant table column. +/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the values. +#[inline] +pub(crate) unsafe fn take_component<'a>( + entity: Entity, + location: EntityLocation, + component_id: ComponentId, + components: &Components, + storages: &'a mut Storages, + removed_components: &mut RemovedComponentEvents, +) -> OwningPtr<'a> { + // SAFETY: component_id is valid (as promised by caller) + let component_info = components.get_info_unchecked(component_id); + removed_components.send(component_id, entity); + match component_info.storage_type() { + StorageType::Table => { + let table = &mut storages.tables[location.table_id]; + // TODO: dense table column index + let components = table.get_column_mut(component_id).unwrap(); + // SAFETY: + // - archetypes never have invalid table rows + // - index is in bounds (as promised by caller) + // - promote is safe because the caller promises to remove the table row without + // dropping its values immediately after this + components + .get_data_unchecked_mut(location.table_row) + .promote() + } + StorageType::SparseSet => storages + .sparse_sets + .get_mut(component_id) + .unwrap() + .remove_and_forget(entity) + .unwrap(), + } +} + +/// # Safety +/// +/// - `component_ids` must only contain valid [`ComponentId`] values unsafe fn initialize_bundle( bundle_type_name: &'static str, components: &Components, @@ -755,5 +717,8 @@ unsafe fn initialize_bundle( panic!("Bundle {bundle_type_name} has duplicate components: {names}"); } - BundleInfo { id, component_ids } + BundleInfo { + id, + component_ids: component_ids.into_boxed_slice(), + } } diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index ade0eb6b7d0a8..d45ae92426aee 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -245,24 +245,19 @@ impl ComponentInfo { } } -/// A semi-opaque value which uniquely identifies the type of a [`Component`] within a -/// [`World`](crate::world::World). -/// -/// Each time a new `Component` type is registered within a `World` using -/// [`World::init_component`](crate::world::World::init_component) or -/// [`World::init_component_with_descriptor`](crate::world::World::init_component_with_descriptor), -/// a corresponding `ComponentId` is created to track it. -/// -/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them -/// into two separate but related concepts allows components to exist outside of Rust's type system. -/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional -/// `ComponentId`s may exist in a `World` to track components which cannot be -/// represented as Rust types for scripting or other advanced use-cases. -/// -/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from -/// one `World` to access the metadata of a `Component` in a different `World` is undefined behaviour -/// and must not be attempted. -#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] +/// An unique ID of a [`Component`] type. +/// +/// Each time a new type is registered using [`World::init_component`](crate::world::World::init_component) +/// or [`World::init_component_with_descriptor`](crate::world::World::init_component_with_descriptor), +/// a unique `ComponentId` is created for it. +/// +/// It may seem like `ComponentId` serves no purpose when [`TypeId`] already exists, but an identifier +/// created at runtime allows for more than just Rust types to be registered as components. +/// +/// **Note:** `ComponentId` values are only valid in the world they come from. Assume that using an +/// `ComponentId` value from one world to access data in another world will either fail or result in +/// undefined behavior. +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] pub struct ComponentId(usize); impl ComponentId { @@ -399,6 +394,7 @@ impl ComponentDescriptor { } } +/// Stores metadata for every known [`Component`]. #[derive(Debug, Default)] pub struct Components { components: Vec, diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 1b1afe3151fc6..cb02dfb479743 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -59,13 +59,14 @@ use std::sync::atomic::AtomicIsize as AtomicIdCursor; #[cfg(not(target_has_atomic = "64"))] type IdCursor = isize; -/// Lightweight identifier of an [entity](crate::entity). +/// The opaque, unique ID of an [entity](crate::entity). /// -/// The identifier is implemented using a [generational index]: a combination of an index and a generation. -/// This allows fast insertion after data removal in an array while minimizing loss of spatial locality. +/// This type is implemented as a [generational index]. Using indices makes random access and +/// memory usage efficient while the versioning prevents aliasing as those indices are recycled. /// -/// These identifiers are only valid on the [`World`] it's sourced from. Attempting to use an `Entity` to -/// fetch entity components or metadata from a different world will either fail or return unexpected results. +/// **Note:** `Entity` values are only valid in the world they come from. Assume that using an +/// `Entity` value from one world to access data in another world will either fail or result in +/// undefined behavior. /// /// [generational index]: https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594 /// @@ -115,6 +116,7 @@ type IdCursor = isize; /// [`EntityCommands`]: crate::system::EntityCommands /// [`Query::get`]: crate::system::Query::get /// [`World`]: crate::world::World +#[repr(C, align(4))] #[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Entity { generation: u32, @@ -283,12 +285,12 @@ impl<'a> Iterator for ReserveEntitiesIterator<'a> { impl<'a> core::iter::ExactSizeIterator for ReserveEntitiesIterator<'a> {} impl<'a> core::iter::FusedIterator for ReserveEntitiesIterator<'a> {} -/// A [`World`]'s internal metadata store on all of its entities. +/// Stores metadata on every [`Entity`] in the [`World`]. /// -/// Contains metadata on: -/// - The generation of every entity. -/// - The alive/dead status of a particular entity. (i.e. "has entity 3 been despawned?") -/// - The location of the entity's components in memory (via [`EntityLocation`]) +/// For each entity, the metadata tracks: +/// - its current generation +/// - if it's dead or alive +/// - the location of its component data (see: [`EntityLocation`]) /// /// [`World`]: crate::world::World #[derive(Debug)] diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 9f00ffd8042db..1e393fb088d9e 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -191,7 +191,7 @@ mod tests { assert_eq!(world.get::(e2).unwrap().0, 42); assert_eq!( - world.entity_mut(e1).remove::().unwrap(), + world.entity_mut(e1).take::().unwrap(), FooBundle { x: TableStored("xyz"), y: SparseStored(123), @@ -240,7 +240,7 @@ mod tests { assert_eq!(world.get::(e3).unwrap().0, 1); assert_eq!(world.get::(e3).unwrap().0, 2); assert_eq!( - world.entity_mut(e3).remove::().unwrap(), + world.entity_mut(e3).take::().unwrap(), NestedBundle { a: A(1), foo: FooBundle { @@ -283,7 +283,7 @@ mod tests { assert_eq!(world.get::(e4), None); assert_eq!( - world.entity_mut(e4).remove::().unwrap(), + world.entity_mut(e4).take::().unwrap(), BundleWithIgnored { c: C, ignored: Ignored, @@ -596,7 +596,7 @@ mod tests { &[(e1, A(1), B(3)), (e2, A(2), B(4))] ); - assert_eq!(world.entity_mut(e1).remove::(), Some(A(1))); + assert_eq!(world.entity_mut(e1).take::(), Some(A(1))); assert_eq!( world .query::<(Entity, &A, &B)>() @@ -656,7 +656,7 @@ mod tests { } for (i, entity) in entities.iter().cloned().enumerate() { - assert_eq!(world.entity_mut(entity).remove::(), Some(A(i))); + assert_eq!(world.entity_mut(entity).take::(), Some(A(i))); } } @@ -675,7 +675,7 @@ mod tests { for (i, entity) in entities.iter().cloned().enumerate() { assert_eq!( - world.entity_mut(entity).remove::(), + world.entity_mut(entity).take::(), Some(SparseStored(i as u32)) ); } @@ -685,7 +685,7 @@ mod tests { fn remove_missing() { let mut world = World::new(); let e = world.spawn((TableStored("abc"), A(123))).id(); - assert!(world.entity_mut(e).remove::().is_none()); + assert!(world.entity_mut(e).take::().is_none()); } #[test] @@ -734,8 +734,8 @@ mod tests { assert_eq!(query.get(&world, b).unwrap(), &TableStored("def")); assert_eq!(query.get(&world, c).unwrap(), &TableStored("ghi")); - world.entity_mut(b).remove::(); - world.entity_mut(c).remove::(); + world.entity_mut(b).take::(); + world.entity_mut(c).take::(); assert_eq!(query.get(&world, a).unwrap(), &TableStored("abc")); assert_eq!(query.get(&world, b).unwrap(), &TableStored("def")); @@ -768,7 +768,7 @@ mod tests { "archetype moves does not result in 'removed component' state" ); - world.entity_mut(b).remove::(); + world.entity_mut(b).take::(); assert_eq!( world.removed::().collect::>(), &[a, b], @@ -1187,7 +1187,7 @@ mod tests { } #[test] - fn remove_intersection() { + fn remove() { let mut world = World::default(); let e1 = world.spawn((A(1), B(1), TableStored("a"))).id(); @@ -1201,7 +1201,7 @@ mod tests { "C is not in the entity, so it should not exist" ); - e.remove_intersection::<(A, B, C)>(); + e.remove::<(A, B, C)>(); assert_eq!( e.get::(), Some(&TableStored("a")), @@ -1225,7 +1225,7 @@ mod tests { } #[test] - fn remove() { + fn take() { let mut world = World::default(); world.spawn((A(1), B(1), TableStored("1"))); let e2 = world.spawn((A(2), B(2), TableStored("2"))).id(); @@ -1238,7 +1238,7 @@ mod tests { .collect::>(); assert_eq!(results, vec![(1, "1"), (2, "2"), (3, "3"),]); - let removed_bundle = world.entity_mut(e2).remove::<(B, TableStored)>().unwrap(); + let removed_bundle = world.entity_mut(e2).take::<(B, TableStored)>().unwrap(); assert_eq!(removed_bundle, (B(2), TableStored("2"))); let results = query diff --git a/crates/bevy_ecs/src/maps.rs b/crates/bevy_ecs/src/maps.rs new file mode 100644 index 0000000000000..f78a5f81edca4 --- /dev/null +++ b/crates/bevy_ecs/src/maps.rs @@ -0,0 +1,374 @@ +mod map { + use std::marker::PhantomData; + + pub trait Index: Copy + Ord + Eq { + fn from_usize(index: usize) -> Self; + fn to_usize(self) -> usize; + } + + pub struct ChunkedSparseArray { + chunks: Vec; CHUNK_SIZE]>>>, + marker: PhantomData, + } + + impl ChunkedSparseArray + where + I: Index, + V: Copy, + { + pub const fn new() -> Self { + Self { + chunks: Vec::new(), + marker: PhantomData, + } + } + + pub fn with_capacity(capacity: usize) -> Self { + let chunks = capacity / CHUNK_SIZE; + Self { + chunks: vec![Some(Self::make_chunk()); chunks], + marker: PhantomData, + } + } + + #[inline] + fn find_chunk(key: I) -> (usize, usize) { + let index = key.to_usize(); + (index / CHUNK_SIZE, index % CHUNK_SIZE) + } + + #[inline] + fn make_chunk() -> Box<[Option; CHUNK_SIZE]> { + Box::new([None; CHUNK_SIZE]) + } + + #[inline] + pub fn capacity(&self) -> usize { + self.chunks.iter().flatten().count() * CHUNK_SIZE + } + + #[inline] + pub fn len(&self) -> usize { + self.chunks + .iter() + .map(|chunk| { + if let Some(options) = chunk { + options.iter().flatten().count() + } else { + 0 + } + }) + .sum() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn clear(&mut self) { + for chunk in self.chunks.iter_mut().flatten() { + chunk.copy_from_slice(&[None; CHUNK_SIZE]); + } + } + + #[inline] + pub fn contains(&self, key: I) -> bool { + self.get(key).is_some() + } + + #[inline] + pub fn get(&self, key: I) -> Option<&V> { + let (chunk, index) = Self::find_chunk(key); + if let Some(maybe_chunk) = self.chunks.get(chunk) { + if let Some(chunk) = maybe_chunk.as_ref() { + // SAFETY: `index` is in bounds + return unsafe { chunk.get_unchecked(index).as_ref() }; + } + } + + None + } + + #[inline] + pub fn get_mut(&mut self, key: I) -> Option<&mut V> { + let (chunk, index) = Self::find_chunk(key); + if let Some(maybe_chunk) = self.chunks.get_mut(chunk) { + if let Some(chunk) = maybe_chunk.as_mut() { + // SAFETY: `index` is in bounds + return unsafe { chunk.get_unchecked_mut(index).as_mut() }; + } + } + + None + } + + #[inline] + pub fn insert(&mut self, key: I, value: V) -> Option { + let (chunk, index) = Self::find_chunk(key); + if chunk >= self.chunks.len() { + self.chunks.resize_with(chunk + 1, || None); + } + + // SAFETY: `chunk` is in bounds + let chunk = unsafe { + self.chunks + .get_unchecked_mut(chunk) + .get_or_insert_with(Self::make_chunk) + }; + + // SAFETY: `index` is in bounds + unsafe { chunk.get_unchecked_mut(index).replace(value) } + } + + #[inline] + pub fn remove(&mut self, key: I) -> Option { + let (chunk, index) = Self::find_chunk(key); + if let Some(maybe_chunk) = self.chunks.get_mut(chunk) { + if let Some(chunk) = maybe_chunk.as_mut() { + // SAFETY: `index` is in bounds + return unsafe { chunk.get_unchecked_mut(index).take() }; + } + } + + None + } + } + + pub struct SparseSet { + sparse: ChunkedSparseArray, + dense: Vec, + data: Vec, + } + + impl SparseSet { + pub const fn new() -> Self { + Self { + sparse: ChunkedSparseArray::new(), + dense: Vec::new(), + data: Vec::new(), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + sparse: ChunkedSparseArray::new(), + dense: Vec::with_capacity(capacity), + data: Vec::with_capacity(capacity), + } + } + + pub fn capacity(&self) -> usize { + self.data.capacity() + } + + #[inline] + pub fn len(&self) -> usize { + self.data.len() + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + #[inline] + pub fn clear(&mut self) { + self.sparse.clear(); + self.dense.clear(); + self.data.clear(); + } + + #[inline] + pub fn contains_key(&self, key: I) -> bool { + self.sparse.contains(key) + } + + #[inline] + pub fn get(&self, key: I) -> Option<&V> { + self.sparse.get(key).and_then(|&dense| { + // SAFETY: `dense` is in bounds + let value = unsafe { self.data.get_unchecked(dense.to_usize()) }; + Some(value) + }) + } + + #[inline] + pub fn get_mut(&mut self, key: I) -> Option<&mut V> { + self.sparse.get(key).and_then(|&dense| { + // SAFETY: `dense` is in bounds + let value = unsafe { self.data.get_unchecked_mut(dense.to_usize()) }; + Some(value) + }) + } + + #[inline] + pub fn insert(&mut self, key: I, value: V) -> Option { + if let Some(dense) = self.sparse.get(key) { + // SAFETY: `dense` is in bounds + let slot = unsafe { self.data.get_unchecked_mut(dense.to_usize()) }; + let previous = std::mem::replace(&mut slot, value); + Some(previous) + } else { + self.sparse.insert(key, self.len()); + self.dense.push(key); + self.data.push(value); + None + } + } + + #[inline] + pub fn remove(&mut self, key: I) -> Option { + if let Some(dense) = self.sparse.remove(key) { + let value = self.data.swap_remove(data); + let swapped_key = self.dense.swap_remove(dense); + *self.sparse.get_mut(swapped_key).unwrap() = dense; + + Some(value) + } else { + None + } + } + + pub fn keys(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn values(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn values_mut(&mut self) -> impl Iterator + '_ { + todo!() + } + + pub fn iter(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + todo!() + } + } + + struct IndexMap { + lo: Box<[Option]>, + hi: SparseSet, + } + + impl IndexMap { + pub fn new() -> Self { + Self { + lo: Vec::with_capacity(ARRAY_SIZE).into_boxed_slice(), + hi: SparseSet::new(), + } + } + + #[inline] + fn is_low_index(&self, index: I) -> bool { + index.to_usize() < self.lo.len() + } + + #[inline] + pub fn capacity(&self) -> usize { + let lo_cap = ARRAY_SIZE; + let hi_cap = self.hi.capacity(); + lo_cap + hi_cap + } + + #[inline] + pub fn len(&self) -> usize { + let lo_len = self.lo.iter().flatten().count(); + let hi_len = self.hi.len(); + lo_len + hi_len + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn clear(&mut self) { + self.lo.iter_mut().for_each(|value| *value = None); + self.hi.clear(); + } + + #[inline] + pub fn contains_key(&self, key: I) -> bool { + self.get(key).is_some() + } + + #[inline] + pub fn get(&self, key: I) -> Option<&V> { + if self.is_low_index(key) { + // SAFETY: `key` is in bounds + unsafe { self.lo.get_unchecked(key.to_usize()).as_ref() } + } else { + self.hi.get(key) + } + } + + #[inline] + pub fn get_mut(&mut self, key: I) -> Option<&mut V> { + if self.is_low_index(key) { + // SAFETY: `key` is in bounds + unsafe { self.lo.get_unchecked_mut(key.to_usize()).as_mut() } + } else { + self.hi.get_mut(key) + } + } + + pub fn get_many_mut(&mut self, keys: [I; N]) -> Option<[&mut V; N]> { + todo!() + } + + pub unsafe fn get_many_unchecked_mut( + &mut self, + keys: [I; N], + ) -> Option<[&mut V; N]> { + todo!() + } + + #[inline] + pub fn insert(&mut self, key: I, value: V) -> Option { + if self.is_low_index(key) { + // SAFETY: `key` is in bounds + unsafe { self.lo.get_unchecked_mut(key.to_usize()).replace(value) } + } else { + self.hi.insert(key, value) + } + } + + #[inline] + pub fn remove(&mut self, key: I) -> Option { + if self.is_low_index(key) { + // SAFETY: `key` is in bounds + unsafe { self.lo.get_unchecked_mut(key.to_usize()).take() } + } else { + self.hi.remove(key) + } + } + + pub fn keys(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn values(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn values_mut(&mut self) -> impl Iterator + '_ { + todo!() + } + + pub fn iter(&self) -> impl Iterator + '_ { + todo!() + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + todo!() + } + } +} diff --git a/crates/bevy_ecs/src/new_archetype.rs b/crates/bevy_ecs/src/new_archetype.rs new file mode 100644 index 0000000000000..1681caf5eb689 --- /dev/null +++ b/crates/bevy_ecs/src/new_archetype.rs @@ -0,0 +1,529 @@ +struct QueryCache { + pub(crate) component_index: HashMap, + pub(crate) matched_archetype_ids: Vec, + // TODO: count how many archetypes share table (remove when count reaches 0) + pub(crate) matched_table_ids: Vec, + pub(crate) access: Access, +} + +struct ComponentRecord { + id: Id, + info: *const TypeInfo, + archetypes: HashMap, + empty_archetypes: HashMap, + tables: HashSet, + empty_tables: HashSet, + relation_info: Option, + parent: Option<*mut ComponentRecord>, + // number of queries that involve this component + ref_count: usize, +} + +struct RelationInfo { + // relationships with the same relation + same_relation_list: LinkedListNode, + // relationships with the same target + same_target_list: LinkedListNode, + // traversible relationships with the same target + same_target_traversable_list: LinkedListNode, + // components that can be inherited from this relationship + reachable: Vec, + // versioning (used to check if `reachable` is valid) + reachable_version: usize, +} + +struct LinkedListNode { + prev: Option<*mut T>, + next: Option<*mut T>, +} + +struct ArchetypeComponentRecord { + id: ArchetypeComponentId, + archetype_id: ArchetypeId, + component_id: ComponentId, + storage: Storage, +} + +struct TableColumn(usize); + +enum Storage { + SparseSet, + Table { + table_id: TableId, + table_column: TableColumn, + table_column_count: usize, + }, +} + +struct QueryDescriptor { + terms: Vec, + /// A query having fixed-source and variable-source terms can lead to situations where the + /// iterator of each field yields a different number of items. + /// + /// When `false`, those longer iterators will be segmented (split apart) into `N` iterators + /// that each yield 1 item. This is the default behavior. + /// + /// When `true`, longer iterators will not be segmented, so take care to use inbound indices. + // TODO: Use iter().cycle().take(n) for fixed-source and variable-source fields? + // TODO: Have to loop over same archetype/table if it has multiple matching columns (and you only asked for one). + // TODO: A (T, U) select term cannot coexist with (T, $var) or (T, *) terms. + // TODO: A (T, U) select term cannot coexist with ($var, U) or (*, U) terms. + instanced: bool, + /// The entity that represents the query. + entity: Option, +} + +enum ObserverKind { + SingleTerm, + MultiTerm { + parent: ObserverId, + parent_term: usize, + }, +} + +pub fn on_event(&mut self, event: Event) { + // all + // -> primary (Id) (e.g. OnSpawn, OnReachableChanged) + // -> secondary (Id) (wildcard, (R, e)) + let event_record = self.event_index.get_mut(event.category).unwrap(); + for id in event.ids { + // <--- component ids + let event_id_record = event_record.by_id[id]; + for observer in event_id_record.observers { + observer.run_callback(event, &mut world); + } + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + ComponentFlags: u8 { + const HasEnableDisable = 1 << 1; + const HasChangeDetection = 1 << 2; + const IsZeroSized = 1 << 3; + const IsPair = 1 << 4; + } +} + +bitflags! { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + ArchetypeFlags: u8 { + const HasObserved = 1 << 0; + const HasPrefab = 1 << 1; + const HasIsA = 1 << 2; + const HasTraversable = 1 << 3; + } +} + +struct FilterBuilder { + stmt: Vec, +} + +impl FilterBuilder { + pub fn new() -> Self { + Self { stmt: Vec::new() } + } + pub fn term(&mut self, term: impl IntoFilterTerm) -> &mut Self { + self + } + pub fn any(&mut self, f: F) -> &mut Self + where + F: FnMut(&mut Self), + { + self + } + pub fn all(&mut self, f: F) -> &mut Self + where + F: FnMut(&mut Self), + { + self + } +} + +enum TermModifier { + Optional, + Not, + Any, + All, + // none of the components on entity + NotOn(Ident), + // any of the components on entity + AnyOn(Ident), + // all of the components on entity + AllOn(Ident), +} + +struct Term { + keyword: TermKeyword, + id: TermId, + source: Option, + // /// The relationship (which must have [`Traversable`]) that can be traversed to find + // /// an instance of the requested component. + // traverse: Option, +} + +enum TermKeyword { + Select, + Optional, + With, + Without, + // filtered + WithNew, + WithChanged, + WithNotNew, + WithNotChanged, +} + +enum TermId { + Entity(TermVar), + Pair(TermVar, TermVar), +} + +enum TermVar { + This, + Fixed(Entity), + Named(&'static str), + Any, + All, +} + +#[derive(Default)] +enum TermAccess { + /// The query will access this data through a shared reference (`&T`). + Ref, + /// The query will access this data through an exclusive reference (`&mut T`). + Mut, + // /// [`Mut`](Access::Mut) for data an entity owns, and [`Ref`](Access::Ref) for data + // /// an entity inherits from traversing a relationship. // *mut T -> &T or &mut T + // #[default] + // OwnedMutInheritedRef, +} + +enum Operation { + Has(bool), + Any, + All, + End, + Yield, +} + +// enum TermFlags { +// // iterate archetypes in BFS order +// Cascade, +// // match by traversing upwards +// Up, +// // match by traversing downwards (derived, cannot be set) +// Down, +// // observer from triggering on term +// Filter, +// // Term ID is an entity +// IsEntity, +// // term ID is a variable +// IsVariable, +// // short for Up(ChildOf) +// Parent, +// // match on self +// MatchOnSelf, +// // match all entities encountered +// TraverseAll, +// TermIdInherited, +// TermMatchAny, +// TermMatchAnySource, +// TermReflexive, +// TermSourceFirstEq, +// TermSourceSecondEq, +// TermTransitive, +// } + +struct Instruction { + operation: Operation, + on_pass: usize, + on_fail: usize, +} + +struct Variable { + name: &'static str, + // binding information + instruction: usize, + index_in_archetype: usize, + index: usize, + count: usize, + value: Option, +} + +struct QueryDescriptor { + terms: Vec, + instructions: Vec, + variables: Vec, +} + +impl Iterator for QueryDescriptor { + type Item = ArchetypeId; + + fn init() { + // collect variables + let mut variables = Vec::new(); + for term in self.terms.iter() { + // TODO: find variables in term + map.entry(key).or_insert_with(|| { + let index = variables.len(); + variables.push(key); + }); + } + } + + fn next(&mut self) -> Option { + // top-level term with $this source that matches the fewest archetypes + let initial_term = self.terms.iter().min_by_key(|map| map.len()); + let initial_term_archetypes = self.terms[initial_term]; + + // iterate archetypes + while let Some(archetype) = initial_term_archetypes.pop() { + self.test(archetype); + } + + None + } + + fn test(&self, archetype: &Archetype) { + let mut v = 0; + let mut next_term = Some(0); + + while let Some(i) = next_term { + if i == initial_term { + // we've already matched this term, skip it + continue; + } + + let inst = &self.instructions[i]; + + match inst.operation { + Has(not) => { + // TODO: determine component ID and source by binding variables + let bind = |var: TermVar| -> Id { + match var { + TermVar::This => { + // + } + TermVar::Fixed(e) => { + e.into(); + } + TermVar::Named(name) => { + // TODO: lookup in map + } + TermVar::All => { + // + } + } + + // ($first, $second)($source) + // ($id)($source) + + // Position($this) -> Position in archetype under test + // ($this, e1) -> test with every entity in archetype under test + // (e2, $this) -> test with every entity in archetype under test + // ($name, e1) -> test with every component in archetype under test + // (e1, $name) -> test with every component in archetype under test + + // TODO: if variable has no binding, bind it + if var.value.is_none() { + let k = var.index_in_archetype; + for (i, comp_id) in archetype.components().enumerate().skip(k) { + if comp_id + /* meets some criteria */ + { + var.value = Some(0); + var.index_in_archetype = i; + var.instruction = inst; + break; + } + } + } + + if var.value.is_none() { + return false; + } + }; + + match term.id { + TermId::Entity(v) => { + bind(v); + } + TermId::Pair(lv, rv) => { + bind(lv); + bind(rv); + } + } + + let source = term.source.map(|src| bind(src)); + let traverse = term.traverse.map(|trv| bind(trv)); + + let component = records.get(id).unwrap(); + let has = component.is_in(archetype); + let success = if not { !has } else { has }; + + if success { + next_term = Some(instruction.on_pass); + } else { + // attempt all possible bindings before backtracking + let mut var = &mut self.variables[v]; + while var.instruction > instruction.on_fail { + var.value = None; + if var.index < (var.count - 1) { + // try another binding + var.index += 1; + break; + } else if v > 0 { + // backtrack to previous variable + v -= 1; + var = &mut variables[v]; + } else { + // no previous variable + return false; + } + } + + next_term = Some(instruction.on_fail.max(var.instruction)); + } + } + Any | All | End => { + // if came from child + // if success + // next_term = Some(instruction.on_pass); + // else if fail + // next_term = Some(instruction.on_fail.max(variable.instruction)); + // else + // next_term += 1; + } + Yield => { + return true; + } + } + } + + return false; + } +} + +pub fn move_entity(entity: Entity, ids: &[ComponentId]) { + for id in ids { + if id.is_traversable() { + // TODO: special IsA handling (instantiate child prefabs) + {} + + // emit events for inherited components + {} + + // traverse up from A (DFS) and rebuild the caches + {} + } + + // TODO: special IsA handling (override, find base value) + {} + + // TODO: special IsA handling (override, copy base value) + {} + } + + // emit events for owned components + {} + + // reachability + let mut stack = Vec::new(); + if records.contains((wildcard, entity)) { + // there are archetypes that can inherit components from this entity + stack.push(entity); + } + + while let Some(e) = stack.pop() { + // traverse down from A (DFS) and invalidate the caches + {} + + let record = records.get((wildcard, e)).unwrap(); + for archetype in record + .archetypes + .iter() + .chain(record.empty_archetypes.iter()) + { + for id in archetype { + // only pairs can be traversed + if !id.is_traversable() { + continue; + } + + // traverse up from A (DFS) and rebuild the caches + {} + + // TODO: compare cache before and after rebuild? + // ideally, after rebuilding the cache, we'd only notify queries observing + // archetypes whose reachable components (or their sources) changed + + // notify observers of "archetype `A` inherits (term) `T` (through relation `R`)" + } + + // ideally we'd only do this the components reachable from `A` changed + for entity in archetype { + if records.contains((wildcard, entity)) { + stack.push(entity); + } + } + } + } +} + +pub fn delete_entity(entity: Entity) { + let mut stack = Vec::new(); + stack.push(entity); + + while let Some(e) = stack.pop() { + // lookup records for e, (e, *), and (*, e) + if let Some(record) = records.get(e) { + for archetype in record + .archetypes + .iter() + .chain(record.empty_archetypes.iter()) + { + if archetype.should_delete() { + info!("despawn cycle encountered"); + continue; + } + + // TODO: OnDeleteTarget for (*, e) + match e.on_delete { + Remove => { + // mark A for transfer to A' + // enqueue "move entities in A" + } + Delete => { + // mark A for deletion + // -subtract 1 from the reference count of this archetype's table + // -remove this archetype's observers + // -remove this archetype's graph edges + // -remove this archetype's graph node + // enqueue "delete entities in A" + for entity in archetype { + stack.push(entity); + } + } + Panic => { + panic!("OnDelete::Panic was set for {}", e); + } + } + } + } + } + + // reverse queue + // flush queue +} + +pub fn table_changes() { + let mut swap = Vec::new(); + while !pending.is_empty() { + for change in pending.drain(..) { + // emit(change) + } + + std::mem::swap(&mut pending, &mut swap); + } +} diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index a8077be288631..718d40b4730b5 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -49,7 +49,17 @@ impl TableId { } } -/// A opaque newtype for rows in [`Table`]s. Specifies a single row in a specific table. +/// A column in a [`Table`]. +/// +/// This is the index of the (conceptual) [`Vec`] that stores all data for a specific component +/// in the [`Table`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct TableColumn(u32); + +/// A row in a [`Table`] +/// +/// This is the index where the data for a single [`Entity`] is stored. /// /// Values of this type are retrievable from [`Archetype::entity_table_row`] and can be /// used alongside [`Archetype::table_id`] to fetch the exact table and row where an @@ -212,17 +222,19 @@ impl Column { #[inline] pub(crate) unsafe fn initialize_from_unchecked( &mut self, - other: &mut Column, - src_row: TableRow, dst_row: TableRow, + src_column: &mut Column, + src_row: TableRow, ) { - debug_assert!(self.data.layout() == other.data.layout()); + debug_assert!(self.data.layout() == src_column.data.layout()); let ptr = self.data.get_unchecked_mut(dst_row.index()); - other.data.swap_remove_unchecked(src_row.index(), ptr); + src_column.data.swap_remove_unchecked(src_row.index(), ptr); + *self.added_ticks.get_unchecked_mut(dst_row.index()) = - other.added_ticks.swap_remove(src_row.index()); + src_column.added_ticks.swap_remove(src_row.index()); + *self.changed_ticks.get_unchecked_mut(dst_row.index()) = - other.changed_ticks.swap_remove(src_row.index()); + src_column.changed_ticks.swap_remove(src_row.index()); } // # Safety @@ -413,8 +425,7 @@ impl TableBuilder { } } -/// A column-oriented [structure-of-arrays] based storage for [`Component`]s of entities -/// in a [`World`]. +/// A column-oriented, [SoA]-based data structure for storing [`Component`] data. /// /// Conceptually, a `Table` can be thought of as an `HashMap`, where /// each `Column` is a type-erased `Vec`. Each row corresponds to a single entity @@ -422,7 +433,7 @@ impl TableBuilder { /// entity). Fetching components from a table involves fetching the associated column for a /// component type (via it's [`ComponentId`]), then fetching the entity's row within that column. /// -/// [structure-of-arrays]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays +/// [SoA]: https://en.wikipedia.org/wiki/AoS_and_SoA#Structure_of_arrays /// [`Component`]: crate::component::Component /// [`World`]: crate::world::World pub struct Table { @@ -436,17 +447,19 @@ impl Table { &self.entities } - /// Removes the entity at the given row and returns the entity swapped in to replace it (if an - /// entity was swapped in) + /// Swap-removes the data at the given row and returns the [`Entity`] of the entity whose + /// data was swapped into that position, if one existed. /// /// # Safety - /// `row` must be in-bounds + /// - `row` must be in-bounds. pub(crate) unsafe fn swap_remove_unchecked(&mut self, row: TableRow) -> Option { + debug_assert!(row.index() < self.entities.len()); + let is_last = row.index() == self.entities.len() - 1; + self.entities.swap_remove(row.index()); for column in self.columns.values_mut() { column.swap_remove_unchecked(row); } - let is_last = row.index() == self.entities.len() - 1; - self.entities.swap_remove(row.index()); + if is_last { None } else { @@ -454,31 +467,30 @@ impl Table { } } - /// Moves the `row` column values to `new_table`, for the columns shared between both tables. - /// Returns the index of the new row in `new_table` and the entity in this table swapped in - /// to replace it (if an entity was swapped in). missing columns will be "forgotten". It is - /// the caller's responsibility to drop them + /// Moves the column values at `row` to `new_table`, leaks any values of columns + /// that `new_table` doesn't have, and returns the [`TableMoveResult`]. /// /// # Safety - /// Row must be in-bounds + /// - `row` must be in-bounds. + /// - The caller is responsible for dropping the leaked values. pub(crate) unsafe fn move_to_and_forget_missing_unchecked( &mut self, row: TableRow, new_table: &mut Table, ) -> TableMoveResult { - debug_assert!(row.index() < self.entity_count()); + debug_assert!(row.index() < self.entities.len()); let is_last = row.index() == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row.index())); for (component_id, column) in self.columns.iter_mut() { if let Some(new_column) = new_table.get_column_mut(*component_id) { - new_column.initialize_from_unchecked(column, row, new_row); + new_column.initialize_from_unchecked(new_row, column, row); } else { - // It's the caller's responsibility to drop these cases. + // It's the caller's responsibility to drop these. let (_, _) = column.swap_remove_and_forget_unchecked(row); } } TableMoveResult { - new_row, + table_row: new_row, swapped_entity: if is_last { None } else { @@ -487,29 +499,49 @@ impl Table { } } - /// Moves the `row` column values to `new_table`, for the columns shared between both tables. - /// Returns the index of the new row in `new_table` and the entity in this table swapped in - /// to replace it (if an entity was swapped in). /// /// # Safety - /// row must be in-bounds + /// + pub(crate) unsafe fn copy_from_unchecked( + &mut self, + src_table: &mut Table, + src_row: TableRow, + ) -> TableRow { + let entity = src_table.entities()[src_row.0 as usize]; + + let dst_row = self.allocate(entity); + for (&component_id, dst_column) in self.columns.iter_mut() { + if let Some(src_column) = src_table.get_column_mut(component_id) { + dst_column.initialize_from_unchecked(dst_row, src_column, src_row) + } + } + + dst_row + } + + /// Moves the column values at `row` to `new_table`, drops any values of columns + /// that `new_table` doesn't have, and returns the [`TableMoveResult`]. + /// + /// + /// # Safety + /// - `row` must be in-bounds pub(crate) unsafe fn move_to_and_drop_missing_unchecked( &mut self, row: TableRow, new_table: &mut Table, ) -> TableMoveResult { - debug_assert!(row.index() < self.entity_count()); + debug_assert!(row.index() < self.entities.len()); let is_last = row.index() == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row.index())); for (component_id, column) in self.columns.iter_mut() { if let Some(new_column) = new_table.get_column_mut(*component_id) { - new_column.initialize_from_unchecked(column, row, new_row); + new_column.initialize_from_unchecked(new_row, column, row); } else { column.swap_remove_unchecked(row); } } TableMoveResult { - new_row, + table_row: new_row, swapped_entity: if is_last { None } else { @@ -518,28 +550,27 @@ impl Table { } } - /// Moves the `row` column values to `new_table`, for the columns shared between both tables. - /// Returns the index of the new row in `new_table` and the entity in this table swapped in - /// to replace it (if an entity was swapped in). + /// Moves the column values at `row` to `new_table` and returns the [`TableMoveResult`]. /// /// # Safety - /// `row` must be in-bounds. `new_table` must contain every component this table has + /// - `row` must be in-bounds. + /// - `new_table` must have a superset of the columns this table has. pub(crate) unsafe fn move_to_superset_unchecked( &mut self, row: TableRow, new_table: &mut Table, ) -> TableMoveResult { - debug_assert!(row.index() < self.entity_count()); + debug_assert!(row.index() < self.entities.len()); let is_last = row.index() == self.entities.len() - 1; let new_row = new_table.allocate(self.entities.swap_remove(row.index())); for (component_id, column) in self.columns.iter_mut() { new_table .get_column_mut(*component_id) .debug_checked_unwrap() - .initialize_from_unchecked(column, row, new_row); + .initialize_from_unchecked(new_row, column, row); } TableMoveResult { - new_row, + table_row: new_row, swapped_entity: if is_last { None } else { @@ -567,19 +598,18 @@ impl Table { if self.entities.capacity() - self.entities.len() < additional { self.entities.reserve(additional); - // use entities vector capacity as driving capacity for all related allocations + // have all columns match the entity vector's capacity let new_capacity = self.entities.capacity(); - for column in self.columns.values_mut() { column.reserve_exact(new_capacity - column.len()); } } } - /// Allocates space for a new entity + /// Allocates a row for the entity's data and returns its index. /// /// # Safety - /// the allocated row must be written to immediately with valid values in each column + /// The caller must immediately write valid values to the columns in the row. pub(crate) unsafe fn allocate(&mut self, entity: Entity) -> TableRow { self.reserve(1); let index = self.entities.len(); @@ -648,9 +678,13 @@ impl Default for Tables { } } +/// The result of transferring an entity from one table to another. +/// +/// Has the row index in the destination table, as well as the entity in that table +/// whose data was originally stored in that row, if one existed. pub(crate) struct TableMoveResult { + pub table_row: TableRow, pub swapped_entity: Option, - pub new_row: TableRow, } impl Tables { @@ -671,6 +705,16 @@ impl Tables { self.tables.get(id.index()) } + #[inline] + pub(crate) fn get_mut(&mut self, id: TableId) -> Option<&mut Table> { + self.tables.get_mut(id.index()) + } + + #[inline] + pub(crate) fn remove(&mut self, id: TableId) -> bool { + todo!() + } + #[inline] pub(crate) fn get_2_mut(&mut self, a: TableId, b: TableId) -> (&mut Table, &mut Table) { if a.index() > b.index() { diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 3761cdb24acbb..73df904177cd1 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -951,9 +951,7 @@ where { fn write(self, world: &mut World) { if let Some(mut entity_mut) = world.get_entity_mut(self.entity) { - // remove intersection to gracefully handle components that were removed before running - // this command - entity_mut.remove_intersection::(); + entity_mut.remove::(); } } } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 5a6f179ceee6b..3a9fda6251fba 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,20 +1,17 @@ use crate::{ - archetype::{Archetype, ArchetypeId, Archetypes}, - bundle::{Bundle, BundleInfo}, + archetype::Archetype, + bundle::Bundle, change_detection::MutUntyped, - component::{Component, ComponentId, ComponentTicks, Components, StorageType}, - entity::{Entities, Entity, EntityLocation}, - removal_detection::RemovedComponentEvents, - storage::Storages, + component::{Component, ComponentId, ComponentTicks}, + entity::{Entity, EntityLocation}, world::{Mut, World}, }; -use bevy_ptr::{OwningPtr, Ptr}; -use bevy_utils::tracing::debug; +use bevy_ptr::Ptr; use std::any::TypeId; use super::unsafe_world_cell::UnsafeEntityCell; -/// A read-only reference to a particular [`Entity`] and all of its components +/// A reference to an entity and its components. #[derive(Copy, Clone)] pub struct EntityRef<'w> { world: &'w World, @@ -70,23 +67,27 @@ impl<'w> EntityRef<'w> { self.world } + /// Returns `true` if the entity has this component. #[inline] pub fn contains(&self) -> bool { self.contains_type_id(TypeId::of::()) } + /// Returns `true` if the entity has this component. #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { self.as_unsafe_world_cell_readonly() .contains_id(component_id) } + /// Returns `true` if the entity has this component. #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { self.as_unsafe_world_cell_readonly() .contains_type_id(type_id) } + /// Returns a reference to the entity's component, if it exists. #[inline] pub fn get(&self) -> Option<&'w T> { // SAFETY: &self implies shared access for duration of returned value @@ -141,7 +142,7 @@ impl<'w> From> for EntityRef<'w> { } } -/// A mutable reference to a particular [`Entity`] and all of its components +/// A mutable reference to an entity and its components. pub struct EntityMut<'w> { world: &'w mut World, entity: Entity, @@ -166,10 +167,8 @@ impl<'w> EntityMut<'w> { /// # Safety /// - /// - `entity` must be valid for `world`: the generation should match that of the entity at the same index. - /// - `location` must be sourced from `world`'s `Entities` and must exactly match the location for `entity` - /// - /// The above is trivially satisfied if `location` was sourced from `world.entities().get(entity)`. + /// - The `entity` is alive. + /// - The `location` belongs to the `entity`. #[inline] pub(crate) unsafe fn new( world: &'w mut World, @@ -186,6 +185,7 @@ impl<'w> EntityMut<'w> { } } + /// Returns this entity's [`Entity`] identifier. #[inline] #[must_use = "Omit the .id() call if you do not need to store the `Entity` identifier."] pub fn id(&self) -> Entity { @@ -202,49 +202,51 @@ impl<'w> EntityMut<'w> { &self.world.archetypes[self.location.archetype_id] } + /// Returns `true` if the entity has this component. #[inline] pub fn contains(&self) -> bool { self.contains_type_id(TypeId::of::()) } + /// Returns `true` if the entity has this component. #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { self.as_unsafe_world_cell_readonly() .contains_id(component_id) } + /// Returns `true` if the entity has this component. #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { self.as_unsafe_world_cell_readonly() .contains_type_id(type_id) } + /// Returns a reference to this the entity has this component. #[inline] pub fn get(&self) -> Option<&'_ T> { // SAFETY: &self implies shared access for duration of returned value unsafe { self.as_unsafe_world_cell_readonly().get::() } } + /// Returns a mutable reference to the entity's component, if it exists. #[inline] pub fn get_mut(&mut self) -> Option> { // SAFETY: &mut self implies exclusive access for duration of returned value unsafe { self.as_unsafe_world_cell().get_mut() } } - /// Retrieves the change ticks for the given component. This can be useful for implementing change - /// detection in custom runtimes. + /// Returns a copy of this component's change ticks. #[inline] pub fn get_change_ticks(&self) -> Option { // SAFETY: &self implies shared access unsafe { self.as_unsafe_world_cell_readonly().get_change_ticks::() } } - /// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change - /// detection in custom runtimes. + /// Returns a copy of this component's change ticks. /// - /// **You should prefer to use the typed API [`EntityMut::get_change_ticks`] where possible and only - /// use this in cases where the actual component types are not known at - /// compile time.** + /// Use the strongly-typed [`get_change_ticks`] when possible and only use this + /// when compile-time type information is not available. #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { // SAFETY: &self implies shared access @@ -256,272 +258,55 @@ impl<'w> EntityMut<'w> { /// Adds a [`Bundle`] of components to the entity. /// - /// This will overwrite any previous value(s) of the same component type. + /// Any components the entity already has will be overwritten. pub fn insert(&mut self, bundle: T) -> &mut Self { - let change_tick = self.world.change_tick(); - let bundle_info = self - .world - .bundles - .init_info::(&mut self.world.components, &mut self.world.storages); - let mut bundle_inserter = bundle_info.get_bundle_inserter( - &mut self.world.entities, - &mut self.world.archetypes, - &mut self.world.components, - &mut self.world.storages, - self.location.archetype_id, - change_tick, - ); - // SAFETY: location matches current entity. `T` matches `bundle_info` - unsafe { - self.location = bundle_inserter.insert(self.entity, self.location, bundle); - } - + // SAFETY: The entity is alive and we're passing in its actual location. + self.location = unsafe { + self.world + .insert_internal::(self.entity, self.location, bundle) + }; self } - // TODO: move to BundleInfo - /// Removes a [`Bundle`] of components from the entity and returns the bundle. - /// - /// Returns `None` if the entity does not contain the bundle. - pub fn remove(&mut self) -> Option { - let archetypes = &mut self.world.archetypes; - let storages = &mut self.world.storages; - let components = &mut self.world.components; - let entities = &mut self.world.entities; - let removed_components = &mut self.world.removed_components; - - let bundle_info = self.world.bundles.init_info::(components, storages); - let old_location = self.location; - // SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid, - // components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T` - let new_archetype_id = unsafe { - remove_bundle_from_archetype( - archetypes, - storages, - components, - old_location.archetype_id, - bundle_info, - false, - )? - }; - - if new_archetype_id == old_location.archetype_id { - return None; - } - - let mut bundle_components = bundle_info.component_ids.iter().cloned(); - let entity = self.entity; - // SAFETY: bundle components are iterated in order, which guarantees that the component type - // matches - let result = unsafe { - T::from_components(storages, &mut |storages| { - let component_id = bundle_components.next().unwrap(); - // SAFETY: - // - entity location is valid - // - table row is removed below, without dropping the contents - // - `components` comes from the same world as `storages` - take_component( - storages, - components, - removed_components, - component_id, - entity, - old_location, - ) - }) - }; - - #[allow(clippy::undocumented_unsafe_blocks)] // TODO: document why this is safe - unsafe { - Self::move_entity_from_remove::( - entity, - &mut self.location, - old_location.archetype_id, - old_location, - entities, - archetypes, - storages, - new_archetype_id, - ); - } - - Some(result) + /// Removes any components in the [`Bundle`] from the entity. + pub fn remove(&mut self) -> &mut Self { + // SAFETY: The entity is alive and we're passing in its actual location. + self.location = unsafe { self.world.remove_internal::(self.entity, self.location) }; + self } - /// Safety: `new_archetype_id` must have the same or a subset of the components - /// in `old_archetype_id`. Probably more safety stuff too, audit a call to - /// this fn as if the code here was written inline + /// Removes all components in the [`Bundle`] from the entity and returns their previous values. /// - /// when DROP is true removed components will be dropped otherwise they will be forgotten - /// - // We use a const generic here so that we are less reliant on - // inlining for rustc to optimize out the `match DROP` - #[allow(clippy::too_many_arguments)] - unsafe fn move_entity_from_remove( - entity: Entity, - self_location: &mut EntityLocation, - old_archetype_id: ArchetypeId, - old_location: EntityLocation, - entities: &mut Entities, - archetypes: &mut Archetypes, - storages: &mut Storages, - new_archetype_id: ArchetypeId, - ) { - let old_archetype = &mut archetypes[old_archetype_id]; - let remove_result = old_archetype.swap_remove(old_location.archetype_row); - if let Some(swapped_entity) = remove_result.swapped_entity { - entities.set(swapped_entity.index(), old_location); - } - 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() { - new_archetype.allocate(entity, old_table_row) - } else { - let (old_table, new_table) = storages - .tables - .get_2_mut(old_table_id, new_archetype.table_id()); - - // SAFETY: old_table_row exists - let move_result = if DROP { - old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) - } else { - old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) - }; - - // SAFETY: move_result.new_row is a valid position in new_archetype's table - let new_location = new_archetype.allocate(entity, move_result.new_row); - - // if an entity was moved into this entity's table spot, update its table row - if let Some(swapped_entity) = move_result.swapped_entity { - let swapped_location = entities.get(swapped_entity).unwrap(); - archetypes[swapped_location.archetype_id] - .set_entity_table_row(swapped_location.archetype_row, old_table_row); - } + /// **Note:** This will not remove any components and will return `None` if the entity does not + /// have all components in the bundle. + pub fn take(&mut self) -> Option { + let (location, result) = + // SAFETY: The entity is alive and we're passing in its actual location. + unsafe { self.world.take_internal::(self.entity, self.location) }; + self.location = location; - new_location - }; - - *self_location = new_location; - // SAFETY: The entity is valid and has been moved to the new location already. - entities.set(entity.index(), new_location); - } - - // TODO: move to BundleInfo - /// Remove any components in the bundle that the entity has. - pub fn remove_intersection(&mut self) { - let archetypes = &mut self.world.archetypes; - let storages = &mut self.world.storages; - let components = &mut self.world.components; - let entities = &mut self.world.entities; - let removed_components = &mut self.world.removed_components; - - let bundle_info = self.world.bundles.init_info::(components, storages); - let old_location = self.location; - - // SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid, - // components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T` - let new_archetype_id = unsafe { - remove_bundle_from_archetype( - archetypes, - storages, - components, - old_location.archetype_id, - bundle_info, - true, - ) - .expect("intersections should always return a result") - }; - - if new_archetype_id == old_location.archetype_id { - return; - } - - let old_archetype = &mut archetypes[old_location.archetype_id]; - let entity = self.entity; - for component_id in bundle_info.component_ids.iter().cloned() { - if old_archetype.contains(component_id) { - removed_components.send(component_id, entity); - - // Make sure to drop components stored in sparse sets. - // Dense components are dropped later in `move_to_and_drop_missing_unchecked`. - if let Some(StorageType::SparseSet) = old_archetype.get_storage_type(component_id) { - storages - .sparse_sets - .get_mut(component_id) - .unwrap() - .remove(entity); - } - } - } - - #[allow(clippy::undocumented_unsafe_blocks)] // TODO: document why this is safe - unsafe { - Self::move_entity_from_remove::( - entity, - &mut self.location, - old_location.archetype_id, - old_location, - entities, - archetypes, - storages, - new_archetype_id, - ); - } + result } + /// Permanently delete the entity and all of its component data. pub fn despawn(self) { - debug!("Despawning entity {:?}", self.entity); - let world = self.world; - world.flush(); - let location = world - .entities - .free(self.entity) - .expect("entity should exist at this point."); - let table_row; - let moved_entity; - - { - let archetype = &mut world.archetypes[location.archetype_id]; - for component_id in archetype.components() { - world.removed_components.send(component_id, self.entity); - } - let remove_result = archetype.swap_remove(location.archetype_row); - if let Some(swapped_entity) = remove_result.swapped_entity { - // SAFETY: swapped_entity is valid and the swapped entity's components are - // moved to the new location immediately after. - unsafe { - world.entities.set(swapped_entity.index(), location); - } - } - table_row = remove_result.table_row; - - for component_id in archetype.sparse_set_components() { - let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap(); - sparse_set.remove(self.entity); - } - // SAFETY: table rows stored in archetypes always exist - moved_entity = unsafe { - world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row) - }; - }; - - if let Some(moved_entity) = moved_entity { - let moved_location = world.entities.get(moved_entity).unwrap(); - world.archetypes[moved_location.archetype_id] - .set_entity_table_row(moved_location.archetype_row, table_row); + self.world.flush(); + // SAFETY: The entity is alive and we're passing in its actual location. + unsafe { + self.world.despawn_internal(self.entity, self.location); } } + /// Returns a reference to the [`World`] that owns this entity. #[inline] pub fn world(&self) -> &World { self.world } - /// Returns this `EntityMut`'s world. + /// Returns the underlying mutable reference to the [`World`]. /// - /// See [`EntityMut::world_scope`] or [`EntityMut::into_world_mut`] for a safe alternative. + /// This function is unsafe because ... + /// See [`world_scope`] or [`into_world_mut`] for safe alternatives. /// /// # Safety /// Caller must not modify the world in a way that changes the current entity's location @@ -532,7 +317,7 @@ impl<'w> EntityMut<'w> { self.world } - /// Return this `EntityMut`'s [`World`], consuming itself. + /// Returns the underlying mutable reference to the [`World`]. #[inline] pub fn into_world_mut(self) -> &'w mut World { self.world @@ -625,167 +410,6 @@ impl<'w> EntityMut<'w> { } } -/// Removes a bundle from the given archetype and returns the resulting archetype (or None if the -/// removal was invalid). in the event that adding the given bundle does not result in an Archetype -/// change. Results are cached in the Archetype Graph to avoid redundant work. -/// if `intersection` is false, attempting to remove a bundle with components _not_ contained in the -/// current archetype will fail, returning None. if `intersection` is true, components in the bundle -/// but not in the current archetype will be ignored -/// -/// # Safety -/// `archetype_id` must exist and components in `bundle_info` must exist -unsafe fn remove_bundle_from_archetype( - archetypes: &mut Archetypes, - storages: &mut Storages, - components: &mut Components, - archetype_id: ArchetypeId, - bundle_info: &BundleInfo, - intersection: bool, -) -> Option { - // check the archetype graph to see if the Bundle has been removed from this archetype in the - // past - let remove_bundle_result = { - let current_archetype = &mut archetypes[archetype_id]; - if intersection { - current_archetype - .edges() - .get_remove_bundle_intersection(bundle_info.id) - } else { - current_archetype.edges().get_remove_bundle(bundle_info.id) - } - }; - let result = if let Some(result) = remove_bundle_result { - // this Bundle removal result is cached. just return that! - result - } else { - let mut next_table_components; - let mut next_sparse_set_components; - let next_table_id; - { - let current_archetype = &mut archetypes[archetype_id]; - let mut removed_table_components = Vec::new(); - let mut removed_sparse_set_components = Vec::new(); - for component_id in bundle_info.component_ids.iter().cloned() { - if current_archetype.contains(component_id) { - // SAFETY: bundle components were already initialized by bundles.get_info - let component_info = components.get_info_unchecked(component_id); - match component_info.storage_type() { - StorageType::Table => removed_table_components.push(component_id), - StorageType::SparseSet => removed_sparse_set_components.push(component_id), - } - } else if !intersection { - // a component in the bundle was not present in the entity's archetype, so this - // removal is invalid cache the result in the archetype - // graph - current_archetype - .edges_mut() - .insert_remove_bundle(bundle_info.id, None); - return None; - } - } - - // sort removed components so we can do an efficient "sorted remove". archetype - // components are already sorted - removed_table_components.sort(); - removed_sparse_set_components.sort(); - next_table_components = current_archetype.table_components().collect(); - next_sparse_set_components = current_archetype.sparse_set_components().collect(); - sorted_remove(&mut next_table_components, &removed_table_components); - sorted_remove( - &mut next_sparse_set_components, - &removed_sparse_set_components, - ); - - next_table_id = if removed_table_components.is_empty() { - current_archetype.table_id() - } else { - // SAFETY: all components in next_table_components exist - storages - .tables - .get_id_or_insert(&next_table_components, components) - }; - } - - let new_archetype_id = archetypes.get_id_or_insert( - next_table_id, - next_table_components, - next_sparse_set_components, - ); - Some(new_archetype_id) - }; - let current_archetype = &mut archetypes[archetype_id]; - // cache the result in an edge - if intersection { - current_archetype - .edges_mut() - .insert_remove_bundle_intersection(bundle_info.id, result); - } else { - current_archetype - .edges_mut() - .insert_remove_bundle(bundle_info.id, result); - } - result -} - -fn sorted_remove(source: &mut Vec, remove: &[T]) { - let mut remove_index = 0; - source.retain(|value| { - while remove_index < remove.len() && *value > remove[remove_index] { - remove_index += 1; - } - - if remove_index < remove.len() { - *value != remove[remove_index] - } else { - true - } - }); -} - -/// Moves component data out of storage. -/// -/// This function leaves the underlying memory unchanged, but the component behind -/// returned pointer is semantically owned by the caller and will not be dropped in its original location. -/// Caller is responsible to drop component data behind returned pointer. -/// -/// # Safety -/// - `location.table_row` must be in bounds of column of component id `component_id` -/// - `component_id` must be valid -/// - `components` must come from the same world as `self` -/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value -#[inline] -pub(crate) unsafe fn take_component<'a>( - storages: &'a mut Storages, - components: &Components, - removed_components: &mut RemovedComponentEvents, - component_id: ComponentId, - entity: Entity, - location: EntityLocation, -) -> OwningPtr<'a> { - // SAFETY: caller promises component_id to be valid - let component_info = components.get_info_unchecked(component_id); - removed_components.send(component_id, entity); - match component_info.storage_type() { - StorageType::Table => { - let table = &mut storages.tables[location.table_id]; - let components = table.get_column_mut(component_id).unwrap(); - // SAFETY: - // - archetypes only store valid table_rows - // - index is in bounds as promised by caller - // - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards - components - .get_data_unchecked_mut(location.table_row) - .promote() - } - StorageType::SparseSet => storages - .sparse_sets - .get_mut(component_id) - .unwrap() - .remove_and_forget(entity) - .unwrap(), - } -} - #[cfg(test)] mod tests { use std::panic::AssertUnwindSafe; @@ -794,27 +418,6 @@ mod tests { use crate::component::ComponentId; use crate::prelude::*; // for the `#[derive(Component)]` - #[test] - fn sorted_remove() { - let mut a = vec![1, 2, 3, 4, 5, 6, 7]; - let b = vec![1, 2, 3, 5, 7]; - super::sorted_remove(&mut a, &b); - - assert_eq!(a, vec![4, 6]); - - let mut a = vec![1]; - let b = vec![1]; - super::sorted_remove(&mut a, &b); - - assert_eq!(a, vec![]); - - let mut a = vec![1]; - let b = vec![2]; - super::sorted_remove(&mut a, &b); - - assert_eq!(a, vec![1]); - } - #[derive(Component)] struct TestComponent(u32); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 0f78884e16944..dafe0e5ce06b5 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -9,10 +9,12 @@ pub use spawn_batch::*; pub use world_cell::*; use crate::{ - archetype::{ArchetypeComponentId, ArchetypeId, ArchetypeRow, Archetypes}, - bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, + archetype::{ArchetypeComponentId, ArchetypeId, ArchetypeRow, Archetypes, MoveAction}, + bundle::{take_component, Bundle, BundleInserter, BundleSpawner, Bundles}, change_detection::{MutUntyped, TicksMut}, - component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components}, + component::{ + Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, StorageType, + }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, event::{Event, Events}, query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery}, @@ -22,7 +24,7 @@ use crate::{ system::Resource, }; use bevy_ptr::{OwningPtr, Ptr}; -use bevy_utils::tracing::warn; +use bevy_utils::tracing::{debug, warn}; use std::{ any::TypeId, fmt, @@ -498,7 +500,7 @@ impl World { let bundle_info = self .bundles .init_info::(&mut self.components, &mut self.storages); - let mut spawner = bundle_info.get_bundle_spawner( + let mut spawner = bundle_info.make_bundle_spawner( &mut self.entities, &mut self.archetypes, &mut self.components, @@ -604,9 +606,221 @@ impl World { unsafe { self.as_unsafe_world_cell().get_entity(entity)?.get_mut() } } - /// Despawns the given `entity`, if it exists. This will also remove all of the entity's - /// [Component]s. Returns `true` if the `entity` is successfully despawned and `false` if - /// the `entity` does not exist. + /// Adds the [`Bundle`] of components to the entity. + /// + /// Any components the entity already has will be overwritten. + pub(crate) fn insert(&mut self, entity: Entity, bundle: T) { + self.flush(); + if let Some(location) = self.entities.get(entity) { + // SAFETY: entity is alive and we have its valid location + unsafe { + self.insert_internal(entity, location, bundle); + } + } else { + warn!( + "error[B0003]: Could not add {} to entity {:?} because it doesn't exist.", + std::any::type_name::(), + entity + ); + } + } + + /// # Safety + /// + /// - The `entity` is alive. + /// - The `location` belongs to the `entity`. + pub(crate) unsafe fn insert_internal( + &mut self, + entity: Entity, + location: EntityLocation, + bundle: T, + ) -> EntityLocation { + let change_tick = self.change_tick(); + let bundle_info = self + .bundles + .init_info::(&mut self.components, &mut self.storages); + + let mut bundle_inserter = bundle_info.make_bundle_inserter( + &mut self.entities, + &mut self.archetypes, + &mut self.components, + &mut self.storages, + location.archetype_id, + change_tick, + ); + + // SAFETY: `location` belongs to `entity` and `bundle_info` belongs to `T` + unsafe { bundle_inserter.insert(entity, location, bundle) } + } + + /// Removes any components in the [`Bundle`] from the entity. + pub(crate) fn remove(&mut self, entity: Entity) { + self.flush(); + if let Some(location) = self.entities.get(entity) { + // SAFETY: entity is alive and we have its valid location + unsafe { + self.remove_internal::(entity, location); + } + } else { + warn!( + "error[B0003]: Could not remove {} from entity {:?} because it doesn't exist.", + std::any::type_name::(), + entity + ); + } + } + + /// # Safety + /// + /// - The `entity` is alive. + /// - The `location` belongs to the `entity`. + pub(crate) unsafe fn remove_internal( + &mut self, + entity: Entity, + location: EntityLocation, + ) -> EntityLocation { + let entities = &mut self.entities; + let components = &mut self.components; + let archetypes = &mut self.archetypes; + let storages = &mut self.storages; + let removed_components = &mut self.removed_components; + + let bundle_info = self.bundles.init_info::(components, storages); + + let (new_archetype_id, _) = archetypes.remove_bundle_from_archetype( + bundle_info.components(), + location.archetype_id, + components, + storages, + true, + ); + + if new_archetype_id == location.archetype_id { + return location; + } + + let old_archetype = archetypes.get_mut(location.archetype_id).unwrap(); + + let mut bundle_components = bundle_info.component_ids.iter().cloned(); + for component_id in bundle_components { + if old_archetype.contains(component_id) { + removed_components.send(component_id, entity); + + // Drop sparse set component. + // Table components will be dropped in `move_entity`. + // TODO: Avoid lookup. Could get storage types from T. + if let Some(StorageType::SparseSet) = old_archetype.get_storage_type(component_id) { + storages + .sparse_sets + .get_mut(component_id) + .unwrap() + .remove(entity); + } + } + } + + // SAFETY: The provided location is correct. The values being removed will be dropped. + unsafe { + archetypes.move_entity::( + entity, + location, + new_archetype_id, + entities, + storages, + ) + } + } + + /// Removes the components in the [`Bundle`] from the entity and returns their values. + /// + /// **Note:** This will not remove any components and will return `None` if the entity does not + /// have all components in the bundle. + pub(crate) fn take(&mut self, entity: Entity) -> Option { + self.flush(); + if let Some(location) = self.entities.get(entity) { + // SAFETY: entity is alive and we have its valid location + let (_, result) = unsafe { self.take_internal::(entity, location) }; + result + } else { + warn!( + "error[B0003]: Could not take {} from entity {:?} because it doesn't exist.", + std::any::type_name::(), + entity + ); + + None + } + } + + /// # Safety + /// + /// - The `entity` is alive. + /// - The `location` belongs to the `entity`. + pub(crate) unsafe fn take_internal( + &mut self, + entity: Entity, + location: EntityLocation, + ) -> (EntityLocation, Option) { + // + let entities = &mut self.entities; + let components = &mut self.components; + let archetypes = &mut self.archetypes; + let storages = &mut self.storages; + let removed_components = &mut self.removed_components; + + let bundle_info = self.bundles.init_info::(components, storages); + + let (new_archetype_id, has_all_components) = archetypes.remove_bundle_from_archetype( + bundle_info.components(), + location.archetype_id, + components, + storages, + true, + ); + + if !has_all_components || (new_archetype_id == location.archetype_id) { + return (location, None); + } + + let mut bundle_components = bundle_info.component_ids.iter().cloned(); + // SAFETY: If `T` is a tuple, its components are always iterated in the same order + // they appear in `T`, which matches what we need to return. + let result = unsafe { + T::read_components(storages, &mut |storages| { + let component_id = bundle_components.next().unwrap(); + // SAFETY: + // - entity location is valid + // - table row is removed below, without dropping the contents + // - `components` comes from the same world as `storages` + take_component( + entity, + location, + component_id, + components, + storages, + removed_components, + ) + }) + }; + + // SAFETY: The provided location is correct. + // We've shallow-copied the values above, so we need to forget them in their + // original location. And only the values we have removed will be forgotten. + let new_location = unsafe { + archetypes.move_entity::( + entity, + location, + new_archetype_id, + entities, + storages, + ) + }; + + (new_location, Some(result)) + } + + /// Permanently deletes `entity` and all of its components, returning `true` if it was + /// successfully deleted and `false` if it did not exist. /// ``` /// use bevy_ecs::{component::Component, world::World}; /// @@ -624,15 +838,81 @@ impl World { /// ``` #[inline] pub fn despawn(&mut self, entity: Entity) -> bool { - if let Some(entity) = self.get_entity_mut(entity) { - entity.despawn(); + self.flush(); + if let Some(location) = self.entities.get(entity) { + // SAFETY: + unsafe { + self.despawn_internal(entity, location); + } true } else { - warn!("error[B0003]: Could not despawn entity {:?} because it doesn't exist in this World.", entity); + warn!( + "error[B0003]: Could not despawn entity {:?} because it doesn't exist.", + entity + ); false } } + /// # Safety + /// + /// - The `entity` is alive. + /// - The `location` belongs to the `entity`. + pub(crate) unsafe fn despawn_internal(&mut self, entity: Entity, location: EntityLocation) { + debug!("Despawning entity {:?}", entity); + let entities = &mut self.entities; + let components = &mut self.components; + let archetypes = &mut self.archetypes; + let storages = &mut self.storages; + let removed_components = &mut self.removed_components; + + // Update events. + let archetype = archetypes.get(location.archetype_id).unwrap(); + for component_id in archetype.components() { + removed_components.send(component_id, entity); + } + + // Drop sparse set components. + for component_id in archetype.sparse_set_components() { + let sparse_set = storages.sparse_sets.get_mut(component_id).unwrap(); + // TODO: ComponentDropped + sparse_set.remove(entity); + } + + // Drop table components. + // SAFETY: The provided location is correct. The values being removed will be dropped. + unsafe { + archetypes.move_entity::( + entity, + location, + ArchetypeId::EMPTY, + entities, + storages, + ); + } + + // Free the entity. + self.entities + .free(entity) + .expect("entity should exist at this point."); + } + + /// TODO + pub fn deferred(&mut self, f: F) + where + F: FnOnce(&mut World), + { + todo!() + } + + pub fn emit(&mut self, event: ()) { + todo!(); + } + + pub fn observer(&mut self, hooks: (), observer: O) { + todo!(); + } + /// Clears the internal component tracker state. /// /// The world maintains some internal state about changed and removed components. This state @@ -1180,6 +1460,7 @@ impl World { let bundle_info = self .bundles .init_info::(&mut self.components, &mut self.storages); + enum SpawnOrInsert<'a, 'b> { Spawn(BundleSpawner<'a, 'b>), Insert(BundleInserter<'a, 'b>, ArchetypeId), @@ -1193,7 +1474,7 @@ impl World { } } } - let mut spawn_or_insert = SpawnOrInsert::Spawn(bundle_info.get_bundle_spawner( + let mut spawn_or_insert = SpawnOrInsert::Spawn(bundle_info.make_bundle_spawner( &mut self.entities, &mut self.archetypes, &mut self.components, @@ -1216,7 +1497,7 @@ impl World { unsafe { inserter.insert(entity, location, bundle) }; } _ => { - let mut inserter = bundle_info.get_bundle_inserter( + let mut inserter = bundle_info.make_bundle_inserter( &mut self.entities, &mut self.archetypes, &mut self.components, @@ -1236,7 +1517,7 @@ impl World { // SAFETY: `entity` is allocated (but non existent), bundle matches inserter unsafe { spawner.spawn_non_existent(entity, bundle) }; } else { - let mut spawner = bundle_info.get_bundle_spawner( + let mut spawner = bundle_info.make_bundle_spawner( &mut self.entities, &mut self.archetypes, &mut self.components, @@ -1450,9 +1731,9 @@ impl World { component_id } - /// Empties queued entities and adds them to the empty [Archetype](crate::archetype::Archetype). + /// Empties queued entities and adds them to the empty [`Archetype`](crate::archetype::Archetype). /// This should be called before doing operations that might operate on queued entities, - /// such as inserting a [Component]. + /// such as inserting a [`Component`]. pub(crate) fn flush(&mut self) { let empty_archetype = self.archetypes.empty_mut(); let table = &mut self.storages.tables[empty_archetype.table_id()]; diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index f5e1bd2792e2b..ab732850e580a 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -32,7 +32,7 @@ where .bundles .init_info::(&mut world.components, &mut world.storages); world.entities.reserve(length as u32); - let mut spawner = bundle_info.get_bundle_spawner( + let mut spawner = bundle_info.make_bundle_spawner( &mut world.entities, &mut world.archetypes, &mut world.components, diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index c795abcfbf107..33102497f2ff9 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -19,6 +19,8 @@ uuid = { version = "1.1", features = ["v4", "serde"] } hashbrown = { version = "0.12", features = ["serde"] } bevy_utils_proc_macros = {version = "0.9.0", path = "macros"} petgraph = "0.6" +generational-arena = "0.2" +slotmap = "1.0" thiserror = "1.0" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index b39ba0f54001d..407b71451fda1 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -24,9 +24,11 @@ pub use ahash::AHasher; pub use bevy_utils_proc_macros::*; pub use default::default; pub use float_ord::*; +pub use generational_arena; pub use hashbrown; pub use instant::{Duration, Instant}; pub use petgraph; +pub use slotmap; pub use thiserror; pub use tracing; pub use uuid::Uuid;