From ab19645ae05106ff3419570c7d5372aaaf772ce9 Mon Sep 17 00:00:00 2001 From: Giacomo Stevanato Date: Fri, 11 Jul 2025 19:24:55 +0200 Subject: [PATCH] Extract bundle info code --- crates/bevy_ecs/src/bundle/info.rs | 570 +++++++++++++++++++++++++++++ crates/bevy_ecs/src/bundle/mod.rs | 569 +--------------------------- 2 files changed, 574 insertions(+), 565 deletions(-) create mode 100644 crates/bevy_ecs/src/bundle/info.rs diff --git a/crates/bevy_ecs/src/bundle/info.rs b/crates/bevy_ecs/src/bundle/info.rs new file mode 100644 index 0000000000..3b25d6c659 --- /dev/null +++ b/crates/bevy_ecs/src/bundle/info.rs @@ -0,0 +1,570 @@ +use alloc::{boxed::Box, vec, vec::Vec}; +use bevy_platform::collections::{HashMap, HashSet}; +use bevy_ptr::OwningPtr; +use bevy_utils::TypeIdMap; +use core::{any::TypeId, ptr::NonNull}; + +use crate::{ + archetype::{Archetype, BundleComponentStatus, ComponentStatus}, + bundle::{Bundle, DynamicBundle}, + change_detection::MaybeLocation, + component::{ + ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, + RequiredComponents, StorageType, Tick, + }, + entity::Entity, + query::DebugCheckedUnwrap as _, + storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, +}; + +/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`]. +/// +/// [`World`]: crate::world::World +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct BundleId(usize); + +impl BundleId { + /// Returns the index of the associated [`Bundle`] type. + /// + /// Note that this is unique per-world, and should not be reused across them. + #[inline] + pub fn index(self) -> usize { + self.0 + } +} + +impl SparseSetIndex for BundleId { + #[inline] + fn sparse_set_index(&self) -> usize { + self.index() + } + + #[inline] + fn get_sparse_set_index(value: usize) -> Self { + Self(value) + } +} + +/// What to do on insertion if a component already exists. +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum InsertMode { + /// Any existing components of a matching type will be overwritten. + Replace, + /// Any existing components of a matching type will be left unchanged. + Keep, +} + +/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`]. +/// +/// [`World`]: crate::world::World +pub struct BundleInfo { + pub(super) id: BundleId, + /// The list of all components contributed by the bundle (including Required Components). This is in + /// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]` + /// + /// # Safety + /// Every ID in this list must be valid within the World that owns the [`BundleInfo`], + /// must have its storage initialized (i.e. columns created in tables, sparse set created), + /// and the range (0..`explicit_components_len`) must be in the same order as the source bundle + /// type writes its components in. + pub(super) component_ids: Vec, + pub(super) required_components: Vec, + pub(super) explicit_components_len: usize, +} + +impl BundleInfo { + /// Create a new [`BundleInfo`]. + /// + /// # Safety + /// + /// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo` + /// and must be in the same order as the source bundle type writes its components in. + unsafe fn new( + bundle_type_name: &'static str, + storages: &mut Storages, + components: &Components, + mut component_ids: Vec, + id: BundleId, + ) -> BundleInfo { + // check for duplicates + let mut deduped = component_ids.clone(); + deduped.sort_unstable(); + deduped.dedup(); + if deduped.len() != component_ids.len() { + // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized + let mut seen = >::default(); + let mut dups = Vec::new(); + for id in component_ids { + if !seen.insert(id) { + dups.push(id); + } + } + + let names = dups + .into_iter() + .map(|id| { + // SAFETY: the caller ensures component_id is valid. + unsafe { components.get_info_unchecked(id).name() } + }) + .collect::>(); + + panic!("Bundle {bundle_type_name} has duplicate components: {names:?}"); + } + + // handle explicit components + let explicit_components_len = component_ids.len(); + let mut required_components = RequiredComponents::default(); + for component_id in component_ids.iter().copied() { + // SAFETY: caller has verified that all ids are valid + let info = unsafe { components.get_info_unchecked(component_id) }; + required_components.merge(info.required_components()); + storages.prepare_component(info); + } + required_components.remove_explicit_components(&component_ids); + + // handle required components + let required_components = required_components + .0 + .into_iter() + .map(|(component_id, v)| { + // Safety: These ids came out of the passed `components`, so they must be valid. + let info = unsafe { components.get_info_unchecked(component_id) }; + storages.prepare_component(info); + // This adds required components to the component_ids list _after_ using that list to remove explicitly provided + // components. This ordering is important! + component_ids.push(component_id); + v.constructor + }) + .collect(); + + // SAFETY: The caller ensures that component_ids: + // - is valid for the associated world + // - has had its storage initialized + // - is in the same order as the source bundle type + BundleInfo { + id, + component_ids, + required_components, + explicit_components_len, + } + } + + /// Returns a value identifying the associated [`Bundle`] type. + #[inline] + pub const fn id(&self) -> BundleId { + self.id + } + + /// Returns the [ID](ComponentId) of each component explicitly defined in this bundle (ex: Required Components are excluded). + /// + /// For all components contributed by this bundle (including Required Components), see [`BundleInfo::contributed_components`] + #[inline] + pub fn explicit_components(&self) -> &[ComponentId] { + &self.component_ids[0..self.explicit_components_len] + } + + /// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are + /// explicitly provided by the bundle. + #[inline] + pub fn required_components(&self) -> &[ComponentId] { + &self.component_ids[self.explicit_components_len..] + } + + /// Returns the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components. + /// + /// For only components explicitly defined in this bundle, see [`BundleInfo::explicit_components`] + #[inline] + pub fn contributed_components(&self) -> &[ComponentId] { + &self.component_ids + } + + /// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components). + /// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`] + #[inline] + pub fn iter_explicit_components(&self) -> impl Iterator + Clone + '_ { + self.explicit_components().iter().copied() + } + + /// Returns an iterator over the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components. + /// + /// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`] + #[inline] + pub fn iter_contributed_components(&self) -> impl Iterator + Clone + '_ { + self.component_ids.iter().copied() + } + + /// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are + /// explicitly provided by the bundle. + pub fn iter_required_components(&self) -> impl Iterator + '_ { + self.required_components().iter().copied() + } + + /// 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 `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`. + /// + /// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`] + /// should be used, which will report `Added` vs `Existing` 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 [`ArchetypeAfterBundleInsert`] 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] + pub(super) unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>( + &self, + table: &mut Table, + sparse_sets: &mut SparseSets, + bundle_component_status: &S, + required_components: impl Iterator, + entity: Entity, + table_row: TableRow, + change_tick: Tick, + bundle: T, + insert_mode: InsertMode, + caller: MaybeLocation, + ) -> T::Effect { + // 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; + let after_effect = bundle.get_components(&mut |storage_type, component_ptr| { + let component_id = *self.component_ids.get_unchecked(bundle_component); + // SAFETY: bundle_component is a valid index for this bundle + let status = unsafe { bundle_component_status.get_status(bundle_component) }; + match storage_type { + StorageType::Table => { + let column = + // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that + // the target table contains the component. + unsafe { table.get_column_mut(component_id).debug_checked_unwrap() }; + match (status, insert_mode) { + (ComponentStatus::Added, _) => { + column.initialize(table_row, component_ptr, change_tick, caller); + } + (ComponentStatus::Existing, InsertMode::Replace) => { + column.replace(table_row, component_ptr, change_tick, caller); + } + (ComponentStatus::Existing, InsertMode::Keep) => { + if let Some(drop_fn) = table.get_drop_for(component_id) { + drop_fn(component_ptr); + } + } + } + } + StorageType::SparseSet => { + let sparse_set = + // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that + // a sparse set exists for the component. + unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; + match (status, insert_mode) { + (ComponentStatus::Added, _) | (_, InsertMode::Replace) => { + sparse_set.insert(entity, component_ptr, change_tick, caller); + } + (ComponentStatus::Existing, InsertMode::Keep) => { + if let Some(drop_fn) = sparse_set.get_drop() { + drop_fn(component_ptr); + } + } + } + } + } + bundle_component += 1; + }); + + for required_component in required_components { + required_component.initialize( + table, + sparse_sets, + change_tick, + table_row, + entity, + caller, + ); + } + + after_effect + } + + /// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called + /// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`]. + /// + /// # Safety + /// + /// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match + /// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized + /// component matching `component_id`. + /// + /// This method _should not_ be called outside of [`BundleInfo::write_components`]. + /// For more information, read the [`BundleInfo::write_components`] safety docs. + /// This function inherits the safety requirements defined there. + pub(crate) unsafe fn initialize_required_component( + table: &mut Table, + sparse_sets: &mut SparseSets, + change_tick: Tick, + table_row: TableRow, + entity: Entity, + component_id: ComponentId, + storage_type: StorageType, + component_ptr: OwningPtr, + caller: MaybeLocation, + ) { + { + match storage_type { + StorageType::Table => { + let column = + // SAFETY: If component_id is in required_components, BundleInfo::new requires that + // the target table contains the component. + unsafe { table.get_column_mut(component_id).debug_checked_unwrap() }; + column.initialize(table_row, component_ptr, change_tick, caller); + } + StorageType::SparseSet => { + let sparse_set = + // SAFETY: If component_id is in required_components, BundleInfo::new requires that + // a sparse set exists for the component. + unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; + sparse_set.insert(entity, component_ptr, change_tick, caller); + } + } + } + } +} + +/// The type of archetype move (or lack thereof) that will result from a bundle +/// being inserted into an entity. +pub(crate) enum ArchetypeMoveType { + /// If the entity already has all of the components that are being inserted, + /// its archetype won't change. + SameArchetype, + /// If only [`sparse set`](StorageType::SparseSet) components are being added, + /// the entity's archetype will change while keeping the same table. + NewArchetypeSameTable { new_archetype: NonNull }, + /// If any [`table-stored`](StorageType::Table) components are being added, + /// both the entity's archetype and table will change. + NewArchetypeNewTable { + new_archetype: NonNull, + new_table: NonNull, + }, +} + +/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world. +#[derive(Default)] +pub struct Bundles { + bundle_infos: Vec, + /// Cache static [`BundleId`] + bundle_ids: TypeIdMap, + /// Cache bundles, which contains both explicit and required components of [`Bundle`] + contributed_bundle_ids: TypeIdMap, + /// Cache dynamic [`BundleId`] with multiple components + dynamic_bundle_ids: HashMap, BundleId>, + dynamic_bundle_storages: HashMap>, + /// Cache optimized dynamic [`BundleId`] with single component + dynamic_component_bundle_ids: HashMap, + dynamic_component_storages: HashMap, +} + +impl Bundles { + /// The total number of [`Bundle`] registered in [`Storages`]. + pub fn len(&self) -> usize { + self.bundle_infos.len() + } + + /// Returns true if no [`Bundle`] registered in [`Storages`]. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Iterate over [`BundleInfo`]. + pub fn iter(&self) -> impl Iterator { + self.bundle_infos.iter() + } + + /// Gets the metadata associated with a specific type of bundle. + /// Returns `None` if the bundle is not registered with the world. + #[inline] + pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> { + self.bundle_infos.get(bundle_id.index()) + } + + /// Gets the value identifying a specific type of bundle. + /// Returns `None` if the bundle does not exist in the world, + /// or if `type_id` does not correspond to a type of bundle. + #[inline] + pub fn get_id(&self, type_id: TypeId) -> Option { + self.bundle_ids.get(&type_id).cloned() + } + + /// Registers a new [`BundleInfo`] for a statically known type. + /// + /// Also registers all the components in the bundle. + pub(crate) fn register_info( + &mut self, + components: &mut ComponentsRegistrator, + storages: &mut Storages, + ) -> BundleId { + let bundle_infos = &mut self.bundle_infos; + *self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { + let mut component_ids= Vec::new(); + T::component_ids(components, &mut |id| component_ids.push(id)); + let id = BundleId(bundle_infos.len()); + let bundle_info = + // SAFETY: T::component_id ensures: + // - its info was created + // - appropriate storage for it has been initialized. + // - it was created in the same order as the components in T + unsafe { BundleInfo::new(core::any::type_name::(), storages, components, component_ids, id) }; + bundle_infos.push(bundle_info); + id + }) + } + + /// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type. + /// + /// Also registers all the components in the bundle. + pub(crate) fn register_contributed_bundle_info( + &mut self, + components: &mut ComponentsRegistrator, + storages: &mut Storages, + ) -> BundleId { + if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::()).cloned() { + id + } else { + let explicit_bundle_id = self.register_info::(components, storages); + // SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this + let id = unsafe { + let (ptr, len) = { + // SAFETY: `explicit_bundle_id` is valid and defined above + let contributed = self + .get_unchecked(explicit_bundle_id) + .contributed_components(); + (contributed.as_ptr(), contributed.len()) + }; + // SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as + // part of init_dynamic_info. No mutable references will be created and the allocation will remain valid. + self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len)) + }; + self.contributed_bundle_ids.insert(TypeId::of::(), id); + id + } + } + + /// # Safety + /// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`. + pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo { + self.bundle_infos.get_unchecked(id.0) + } + + /// # Safety + /// This [`BundleId`] must have been initialized with a single [`Component`] (via [`init_component_info`](Self::init_dynamic_info)) + pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType { + *self + .dynamic_component_storages + .get(&id) + .debug_checked_unwrap() + } + + /// # Safety + /// This [`BundleId`] must have been initialized with multiple [`Component`]s (via [`init_dynamic_info`](Self::init_dynamic_info)) + pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec { + self.dynamic_bundle_storages + .get_mut(&id) + .debug_checked_unwrap() + } + + /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`]. + /// + /// # Panics + /// + /// Panics if any of the provided [`ComponentId`]s do not exist in the + /// provided [`Components`]. + pub(crate) fn init_dynamic_info( + &mut self, + storages: &mut Storages, + components: &Components, + component_ids: &[ComponentId], + ) -> BundleId { + let bundle_infos = &mut self.bundle_infos; + + // Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry` + let (_, bundle_id) = self + .dynamic_bundle_ids + .raw_entry_mut() + .from_key(component_ids) + .or_insert_with(|| { + let (id, storages) = initialize_dynamic_bundle( + bundle_infos, + storages, + components, + Vec::from(component_ids), + ); + // SAFETY: The ID always increases when new bundles are added, and so, the ID is unique. + unsafe { + self.dynamic_bundle_storages + .insert_unique_unchecked(id, storages); + } + (component_ids.into(), id) + }); + *bundle_id + } + + /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component. + /// + /// # Panics + /// + /// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`]. + pub(crate) fn init_component_info( + &mut self, + storages: &mut Storages, + components: &Components, + component_id: ComponentId, + ) -> BundleId { + let bundle_infos = &mut self.bundle_infos; + let bundle_id = self + .dynamic_component_bundle_ids + .entry(component_id) + .or_insert_with(|| { + let (id, storage_type) = initialize_dynamic_bundle( + bundle_infos, + storages, + components, + vec![component_id], + ); + self.dynamic_component_storages.insert(id, storage_type[0]); + id + }); + *bundle_id + } +} + +/// Asserts that all components are part of [`Components`] +/// and initializes a [`BundleInfo`]. +fn initialize_dynamic_bundle( + bundle_infos: &mut Vec, + storages: &mut Storages, + components: &Components, + component_ids: Vec, +) -> (BundleId, Vec) { + // Assert component existence + let storage_types = component_ids.iter().map(|&id| { + components.get_info(id).unwrap_or_else(|| { + panic!( + "init_dynamic_info called with component id {id:?} which doesn't exist in this world" + ) + }).storage_type() + }).collect(); + + let id = BundleId(bundle_infos.len()); + let bundle_info = + // SAFETY: `component_ids` are valid as they were just checked + unsafe { BundleInfo::new("", storages, components, component_ids, id) }; + bundle_infos.push(bundle_info); + + (id, storage_types) +} diff --git a/crates/bevy_ecs/src/bundle/mod.rs b/crates/bevy_ecs/src/bundle/mod.rs index 3971bf2816..f195bf2410 100644 --- a/crates/bevy_ecs/src/bundle/mod.rs +++ b/crates/bevy_ecs/src/bundle/mod.rs @@ -3,6 +3,7 @@ //! This module contains the [`Bundle`] trait and some other helper types. mod impls; +mod info; mod insert; mod remove; mod spawner; @@ -13,6 +14,8 @@ pub(crate) use insert::BundleInserter; pub(crate) use remove::BundleRemover; pub(crate) use spawner::BundleSpawner; +pub use info::*; + /// Derive the [`Bundle`] trait /// /// You can apply this derive macro to structs that are @@ -67,22 +70,10 @@ pub(crate) use spawner::BundleSpawner; pub use bevy_ecs_macros::Bundle; use crate::{ - archetype::{Archetype, BundleComponentStatus, ComponentStatus}, - change_detection::MaybeLocation, - component::{ - ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor, - RequiredComponents, StorageType, Tick, - }, - entity::Entity, - query::DebugCheckedUnwrap, - storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, + component::{ComponentId, Components, ComponentsRegistrator, StorageType}, world::EntityWorldMut, }; -use alloc::{boxed::Box, vec, vec::Vec}; -use bevy_platform::collections::{HashMap, HashSet}; use bevy_ptr::OwningPtr; -use bevy_utils::TypeIdMap; -use core::{any::TypeId, ptr::NonNull}; /// The `Bundle` trait enables insertion and removal of [`Component`]s from an entity. /// @@ -271,555 +262,3 @@ pub trait BundleEffect { /// A trait implemented for [`BundleEffect`] implementations that do nothing. This is used as a type constraint for /// [`Bundle`] APIs that do not / cannot run [`DynamicBundle::Effect`], such as "batch spawn" APIs. pub trait NoBundleEffect {} - -/// For a specific [`World`], this stores a unique value identifying a type of a registered [`Bundle`]. -/// -/// [`World`]: crate::world::World -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] -pub struct BundleId(usize); - -impl BundleId { - /// Returns the index of the associated [`Bundle`] type. - /// - /// Note that this is unique per-world, and should not be reused across them. - #[inline] - pub fn index(self) -> usize { - self.0 - } -} - -impl SparseSetIndex for BundleId { - #[inline] - fn sparse_set_index(&self) -> usize { - self.index() - } - - #[inline] - fn get_sparse_set_index(value: usize) -> Self { - Self(value) - } -} - -/// What to do on insertion if a component already exists. -#[derive(Clone, Copy, Eq, PartialEq)] -pub enum InsertMode { - /// Any existing components of a matching type will be overwritten. - Replace, - /// Any existing components of a matching type will be left unchanged. - Keep, -} - -/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`]. -/// -/// [`World`]: crate::world::World -pub struct BundleInfo { - id: BundleId, - /// The list of all components contributed by the bundle (including Required Components). This is in - /// the order `[EXPLICIT_COMPONENTS][REQUIRED_COMPONENTS]` - /// - /// # Safety - /// Every ID in this list must be valid within the World that owns the [`BundleInfo`], - /// must have its storage initialized (i.e. columns created in tables, sparse set created), - /// and the range (0..`explicit_components_len`) must be in the same order as the source bundle - /// type writes its components in. - component_ids: Vec, - required_components: Vec, - explicit_components_len: usize, -} - -impl BundleInfo { - /// Create a new [`BundleInfo`]. - /// - /// # Safety - /// - /// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo` - /// and must be in the same order as the source bundle type writes its components in. - unsafe fn new( - bundle_type_name: &'static str, - storages: &mut Storages, - components: &Components, - mut component_ids: Vec, - id: BundleId, - ) -> BundleInfo { - // check for duplicates - let mut deduped = component_ids.clone(); - deduped.sort_unstable(); - deduped.dedup(); - if deduped.len() != component_ids.len() { - // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized - let mut seen = >::default(); - let mut dups = Vec::new(); - for id in component_ids { - if !seen.insert(id) { - dups.push(id); - } - } - - let names = dups - .into_iter() - .map(|id| { - // SAFETY: the caller ensures component_id is valid. - unsafe { components.get_info_unchecked(id).name() } - }) - .collect::>(); - - panic!("Bundle {bundle_type_name} has duplicate components: {names:?}"); - } - - // handle explicit components - let explicit_components_len = component_ids.len(); - let mut required_components = RequiredComponents::default(); - for component_id in component_ids.iter().copied() { - // SAFETY: caller has verified that all ids are valid - let info = unsafe { components.get_info_unchecked(component_id) }; - required_components.merge(info.required_components()); - storages.prepare_component(info); - } - required_components.remove_explicit_components(&component_ids); - - // handle required components - let required_components = required_components - .0 - .into_iter() - .map(|(component_id, v)| { - // Safety: These ids came out of the passed `components`, so they must be valid. - let info = unsafe { components.get_info_unchecked(component_id) }; - storages.prepare_component(info); - // This adds required components to the component_ids list _after_ using that list to remove explicitly provided - // components. This ordering is important! - component_ids.push(component_id); - v.constructor - }) - .collect(); - - // SAFETY: The caller ensures that component_ids: - // - is valid for the associated world - // - has had its storage initialized - // - is in the same order as the source bundle type - BundleInfo { - id, - component_ids, - required_components, - explicit_components_len, - } - } - - /// Returns a value identifying the associated [`Bundle`] type. - #[inline] - pub const fn id(&self) -> BundleId { - self.id - } - - /// Returns the [ID](ComponentId) of each component explicitly defined in this bundle (ex: Required Components are excluded). - /// - /// For all components contributed by this bundle (including Required Components), see [`BundleInfo::contributed_components`] - #[inline] - pub fn explicit_components(&self) -> &[ComponentId] { - &self.component_ids[0..self.explicit_components_len] - } - - /// Returns the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are - /// explicitly provided by the bundle. - #[inline] - pub fn required_components(&self) -> &[ComponentId] { - &self.component_ids[self.explicit_components_len..] - } - - /// Returns the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components. - /// - /// For only components explicitly defined in this bundle, see [`BundleInfo::explicit_components`] - #[inline] - pub fn contributed_components(&self) -> &[ComponentId] { - &self.component_ids - } - - /// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components). - /// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`] - #[inline] - pub fn iter_explicit_components(&self) -> impl Iterator + Clone + '_ { - self.explicit_components().iter().copied() - } - - /// Returns an iterator over the [ID](ComponentId) of each component contributed by this bundle. This includes Required Components. - /// - /// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`] - #[inline] - pub fn iter_contributed_components(&self) -> impl Iterator + Clone + '_ { - self.component_ids.iter().copied() - } - - /// Returns an iterator over the [ID](ComponentId) of each Required Component needed by this bundle. This _does not include_ Required Components that are - /// explicitly provided by the bundle. - pub fn iter_required_components(&self) -> impl Iterator + '_ { - self.required_components().iter().copied() - } - - /// 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 `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`. - /// - /// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`] - /// should be used, which will report `Added` vs `Existing` 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 [`ArchetypeAfterBundleInsert`] 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] - unsafe fn write_components<'a, T: DynamicBundle, S: BundleComponentStatus>( - &self, - table: &mut Table, - sparse_sets: &mut SparseSets, - bundle_component_status: &S, - required_components: impl Iterator, - entity: Entity, - table_row: TableRow, - change_tick: Tick, - bundle: T, - insert_mode: InsertMode, - caller: MaybeLocation, - ) -> T::Effect { - // 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; - let after_effect = bundle.get_components(&mut |storage_type, component_ptr| { - let component_id = *self.component_ids.get_unchecked(bundle_component); - // SAFETY: bundle_component is a valid index for this bundle - let status = unsafe { bundle_component_status.get_status(bundle_component) }; - match storage_type { - StorageType::Table => { - let column = - // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that - // the target table contains the component. - unsafe { table.get_column_mut(component_id).debug_checked_unwrap() }; - match (status, insert_mode) { - (ComponentStatus::Added, _) => { - column.initialize(table_row, component_ptr, change_tick, caller); - } - (ComponentStatus::Existing, InsertMode::Replace) => { - column.replace(table_row, component_ptr, change_tick, caller); - } - (ComponentStatus::Existing, InsertMode::Keep) => { - if let Some(drop_fn) = table.get_drop_for(component_id) { - drop_fn(component_ptr); - } - } - } - } - StorageType::SparseSet => { - let sparse_set = - // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that - // a sparse set exists for the component. - unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; - match (status, insert_mode) { - (ComponentStatus::Added, _) | (_, InsertMode::Replace) => { - sparse_set.insert(entity, component_ptr, change_tick, caller); - } - (ComponentStatus::Existing, InsertMode::Keep) => { - if let Some(drop_fn) = sparse_set.get_drop() { - drop_fn(component_ptr); - } - } - } - } - } - bundle_component += 1; - }); - - for required_component in required_components { - required_component.initialize( - table, - sparse_sets, - change_tick, - table_row, - entity, - caller, - ); - } - - after_effect - } - - /// Internal method to initialize a required component from an [`OwningPtr`]. This should ultimately be called - /// in the context of [`BundleInfo::write_components`], via [`RequiredComponentConstructor::initialize`]. - /// - /// # Safety - /// - /// `component_ptr` must point to a required component value that matches the given `component_id`. The `storage_type` must match - /// the type associated with `component_id`. The `entity` and `table_row` must correspond to an entity with an uninitialized - /// component matching `component_id`. - /// - /// This method _should not_ be called outside of [`BundleInfo::write_components`]. - /// For more information, read the [`BundleInfo::write_components`] safety docs. - /// This function inherits the safety requirements defined there. - pub(crate) unsafe fn initialize_required_component( - table: &mut Table, - sparse_sets: &mut SparseSets, - change_tick: Tick, - table_row: TableRow, - entity: Entity, - component_id: ComponentId, - storage_type: StorageType, - component_ptr: OwningPtr, - caller: MaybeLocation, - ) { - { - match storage_type { - StorageType::Table => { - let column = - // SAFETY: If component_id is in required_components, BundleInfo::new requires that - // the target table contains the component. - unsafe { table.get_column_mut(component_id).debug_checked_unwrap() }; - column.initialize(table_row, component_ptr, change_tick, caller); - } - StorageType::SparseSet => { - let sparse_set = - // SAFETY: If component_id is in required_components, BundleInfo::new requires that - // a sparse set exists for the component. - unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; - sparse_set.insert(entity, component_ptr, change_tick, caller); - } - } - } - } -} - -/// The type of archetype move (or lack thereof) that will result from a bundle -/// being inserted into an entity. -pub(crate) enum ArchetypeMoveType { - /// If the entity already has all of the components that are being inserted, - /// its archetype won't change. - SameArchetype, - /// If only [`sparse set`](StorageType::SparseSet) components are being added, - /// the entity's archetype will change while keeping the same table. - NewArchetypeSameTable { new_archetype: NonNull }, - /// If any [`table-stored`](StorageType::Table) components are being added, - /// both the entity's archetype and table will change. - NewArchetypeNewTable { - new_archetype: NonNull, - new_table: NonNull
, - }, -} - -/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world. -#[derive(Default)] -pub struct Bundles { - bundle_infos: Vec, - /// Cache static [`BundleId`] - bundle_ids: TypeIdMap, - /// Cache bundles, which contains both explicit and required components of [`Bundle`] - contributed_bundle_ids: TypeIdMap, - /// Cache dynamic [`BundleId`] with multiple components - dynamic_bundle_ids: HashMap, BundleId>, - dynamic_bundle_storages: HashMap>, - /// Cache optimized dynamic [`BundleId`] with single component - dynamic_component_bundle_ids: HashMap, - dynamic_component_storages: HashMap, -} - -impl Bundles { - /// The total number of [`Bundle`] registered in [`Storages`]. - pub fn len(&self) -> usize { - self.bundle_infos.len() - } - - /// Returns true if no [`Bundle`] registered in [`Storages`]. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Iterate over [`BundleInfo`]. - pub fn iter(&self) -> impl Iterator { - self.bundle_infos.iter() - } - - /// Gets the metadata associated with a specific type of bundle. - /// Returns `None` if the bundle is not registered with the world. - #[inline] - pub fn get(&self, bundle_id: BundleId) -> Option<&BundleInfo> { - self.bundle_infos.get(bundle_id.index()) - } - - /// Gets the value identifying a specific type of bundle. - /// Returns `None` if the bundle does not exist in the world, - /// or if `type_id` does not correspond to a type of bundle. - #[inline] - pub fn get_id(&self, type_id: TypeId) -> Option { - self.bundle_ids.get(&type_id).cloned() - } - - /// Registers a new [`BundleInfo`] for a statically known type. - /// - /// Also registers all the components in the bundle. - pub(crate) fn register_info( - &mut self, - components: &mut ComponentsRegistrator, - storages: &mut Storages, - ) -> BundleId { - let bundle_infos = &mut self.bundle_infos; - *self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { - let mut component_ids= Vec::new(); - T::component_ids(components, &mut |id| component_ids.push(id)); - let id = BundleId(bundle_infos.len()); - let bundle_info = - // SAFETY: T::component_id ensures: - // - its info was created - // - appropriate storage for it has been initialized. - // - it was created in the same order as the components in T - unsafe { BundleInfo::new(core::any::type_name::(), storages, components, component_ids, id) }; - bundle_infos.push(bundle_info); - id - }) - } - - /// Registers a new [`BundleInfo`], which contains both explicit and required components for a statically known type. - /// - /// Also registers all the components in the bundle. - pub(crate) fn register_contributed_bundle_info( - &mut self, - components: &mut ComponentsRegistrator, - storages: &mut Storages, - ) -> BundleId { - if let Some(id) = self.contributed_bundle_ids.get(&TypeId::of::()).cloned() { - id - } else { - let explicit_bundle_id = self.register_info::(components, storages); - // SAFETY: reading from `explicit_bundle_id` and creating new bundle in same time. Its valid because bundle hashmap allow this - let id = unsafe { - let (ptr, len) = { - // SAFETY: `explicit_bundle_id` is valid and defined above - let contributed = self - .get_unchecked(explicit_bundle_id) - .contributed_components(); - (contributed.as_ptr(), contributed.len()) - }; - // SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as - // part of init_dynamic_info. No mutable references will be created and the allocation will remain valid. - self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len)) - }; - self.contributed_bundle_ids.insert(TypeId::of::(), id); - id - } - } - - /// # Safety - /// A [`BundleInfo`] with the given [`BundleId`] must have been initialized for this instance of `Bundles`. - pub(crate) unsafe fn get_unchecked(&self, id: BundleId) -> &BundleInfo { - self.bundle_infos.get_unchecked(id.0) - } - - /// # Safety - /// This [`BundleId`] must have been initialized with a single [`Component`] (via [`init_component_info`](Self::init_dynamic_info)) - pub(crate) unsafe fn get_storage_unchecked(&self, id: BundleId) -> StorageType { - *self - .dynamic_component_storages - .get(&id) - .debug_checked_unwrap() - } - - /// # Safety - /// This [`BundleId`] must have been initialized with multiple [`Component`]s (via [`init_dynamic_info`](Self::init_dynamic_info)) - pub(crate) unsafe fn get_storages_unchecked(&mut self, id: BundleId) -> &mut Vec { - self.dynamic_bundle_storages - .get_mut(&id) - .debug_checked_unwrap() - } - - /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`]. - /// - /// # Panics - /// - /// Panics if any of the provided [`ComponentId`]s do not exist in the - /// provided [`Components`]. - pub(crate) fn init_dynamic_info( - &mut self, - storages: &mut Storages, - components: &Components, - component_ids: &[ComponentId], - ) -> BundleId { - let bundle_infos = &mut self.bundle_infos; - - // Use `raw_entry_mut` to avoid cloning `component_ids` to access `Entry` - let (_, bundle_id) = self - .dynamic_bundle_ids - .raw_entry_mut() - .from_key(component_ids) - .or_insert_with(|| { - let (id, storages) = initialize_dynamic_bundle( - bundle_infos, - storages, - components, - Vec::from(component_ids), - ); - // SAFETY: The ID always increases when new bundles are added, and so, the ID is unique. - unsafe { - self.dynamic_bundle_storages - .insert_unique_unchecked(id, storages); - } - (component_ids.into(), id) - }); - *bundle_id - } - - /// Initializes a new [`BundleInfo`] for a dynamic [`Bundle`] with single component. - /// - /// # Panics - /// - /// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`]. - pub(crate) fn init_component_info( - &mut self, - storages: &mut Storages, - components: &Components, - component_id: ComponentId, - ) -> BundleId { - let bundle_infos = &mut self.bundle_infos; - let bundle_id = self - .dynamic_component_bundle_ids - .entry(component_id) - .or_insert_with(|| { - let (id, storage_type) = initialize_dynamic_bundle( - bundle_infos, - storages, - components, - vec![component_id], - ); - self.dynamic_component_storages.insert(id, storage_type[0]); - id - }); - *bundle_id - } -} - -/// Asserts that all components are part of [`Components`] -/// and initializes a [`BundleInfo`]. -fn initialize_dynamic_bundle( - bundle_infos: &mut Vec, - storages: &mut Storages, - components: &Components, - component_ids: Vec, -) -> (BundleId, Vec) { - // Assert component existence - let storage_types = component_ids.iter().map(|&id| { - components.get_info(id).unwrap_or_else(|| { - panic!( - "init_dynamic_info called with component id {id:?} which doesn't exist in this world" - ) - }).storage_type() - }).collect(); - - let id = BundleId(bundle_infos.len()); - let bundle_info = - // SAFETY: `component_ids` are valid as they were just checked - unsafe { BundleInfo::new("", storages, components, component_ids, id) }; - bundle_infos.push(bundle_info); - - (id, storage_types) -}