From d017bf487a77abb4a0c36747e4e417cd731062e5 Mon Sep 17 00:00:00 2001 From: Giacomo Stevanato Date: Thu, 10 Jul 2025 19:35:15 +0200 Subject: [PATCH] Extract bundle insertion code --- crates/bevy_ecs/src/bundle/insert.rs | 544 +++++++++++++++++++++++++++ crates/bevy_ecs/src/bundle/mod.rs | 527 +------------------------- 2 files changed, 547 insertions(+), 524 deletions(-) create mode 100644 crates/bevy_ecs/src/bundle/insert.rs diff --git a/crates/bevy_ecs/src/bundle/insert.rs b/crates/bevy_ecs/src/bundle/insert.rs new file mode 100644 index 0000000000..bf8a99a7b1 --- /dev/null +++ b/crates/bevy_ecs/src/bundle/insert.rs @@ -0,0 +1,544 @@ +use alloc::vec::Vec; +use bevy_ptr::ConstNonNull; +use core::ptr::NonNull; + +use crate::{ + archetype::{ + Archetype, ArchetypeAfterBundleInsert, ArchetypeCreated, ArchetypeId, Archetypes, + ComponentStatus, + }, + bundle::{ArchetypeMoveType, Bundle, BundleId, BundleInfo, DynamicBundle, InsertMode}, + change_detection::MaybeLocation, + component::{Components, ComponentsRegistrator, StorageType, Tick}, + entity::{Entities, Entity, EntityLocation}, + lifecycle::{ADD, INSERT, REPLACE}, + observer::Observers, + query::DebugCheckedUnwrap as _, + relationship::RelationshipHookMode, + storage::{Storages, Table}, + world::{unsafe_world_cell::UnsafeWorldCell, World}, +}; + +// SAFETY: We have exclusive world access so our pointers can't be invalidated externally +pub(crate) struct BundleInserter<'w> { + world: UnsafeWorldCell<'w>, + bundle_info: ConstNonNull, + archetype_after_insert: ConstNonNull, + table: NonNull, + archetype: NonNull, + archetype_move_type: ArchetypeMoveType, + change_tick: Tick, +} + +impl<'w> BundleInserter<'w> { + #[inline] + pub(crate) fn new( + world: &'w mut World, + archetype_id: ArchetypeId, + change_tick: Tick, + ) -> Self { + // SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too. + let mut registrator = + unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) }; + let bundle_id = world + .bundles + .register_info::(&mut registrator, &mut world.storages); + // SAFETY: We just ensured this bundle exists + unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) } + } + + /// Creates a new [`BundleInserter`]. + /// + /// # Safety + /// - Caller must ensure that `bundle_id` exists in `world.bundles`. + #[inline] + pub(crate) unsafe fn new_with_id( + world: &'w mut World, + archetype_id: ArchetypeId, + bundle_id: BundleId, + change_tick: Tick, + ) -> Self { + // SAFETY: We will not make any accesses to the command queue, component or resource data of this world + let bundle_info = world.bundles.get_unchecked(bundle_id); + let bundle_id = bundle_info.id(); + let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype( + &mut world.archetypes, + &mut world.storages, + &world.components, + &world.observers, + archetype_id, + ); + + let inserter = if new_archetype_id == archetype_id { + let archetype = &mut world.archetypes[archetype_id]; + // SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype + let archetype_after_insert = unsafe { + archetype + .edges() + .get_archetype_after_bundle_insert_internal(bundle_id) + .debug_checked_unwrap() + }; + let table_id = archetype.table_id(); + let table = &mut world.storages.tables[table_id]; + Self { + archetype_after_insert: archetype_after_insert.into(), + archetype: archetype.into(), + bundle_info: bundle_info.into(), + table: table.into(), + archetype_move_type: ArchetypeMoveType::SameArchetype, + change_tick, + world: world.as_unsafe_world_cell(), + } + } else { + let (archetype, new_archetype) = + world.archetypes.get_2_mut(archetype_id, new_archetype_id); + // SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype + let archetype_after_insert = unsafe { + archetype + .edges() + .get_archetype_after_bundle_insert_internal(bundle_id) + .debug_checked_unwrap() + }; + let table_id = archetype.table_id(); + let new_table_id = new_archetype.table_id(); + if table_id == new_table_id { + let table = &mut world.storages.tables[table_id]; + Self { + archetype_after_insert: archetype_after_insert.into(), + archetype: archetype.into(), + bundle_info: bundle_info.into(), + table: table.into(), + archetype_move_type: ArchetypeMoveType::NewArchetypeSameTable { + new_archetype: new_archetype.into(), + }, + change_tick, + world: world.as_unsafe_world_cell(), + } + } else { + let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id); + Self { + archetype_after_insert: archetype_after_insert.into(), + archetype: archetype.into(), + bundle_info: bundle_info.into(), + table: table.into(), + archetype_move_type: ArchetypeMoveType::NewArchetypeNewTable { + new_archetype: new_archetype.into(), + new_table: new_table.into(), + }, + change_tick, + world: world.as_unsafe_world_cell(), + } + } + }; + + if is_new_created { + inserter + .world + .into_deferred() + .trigger(ArchetypeCreated(new_archetype_id)); + } + inserter + } + + /// # Safety + /// `entity` must currently exist in the source archetype for this inserter. `location` + /// must be `entity`'s location in the archetype. `T` must match this [`BundleInfo`]'s type + #[inline] + pub(crate) unsafe fn insert( + &mut self, + entity: Entity, + location: EntityLocation, + bundle: T, + insert_mode: InsertMode, + caller: MaybeLocation, + relationship_hook_mode: RelationshipHookMode, + ) -> (EntityLocation, T::Effect) { + let bundle_info = self.bundle_info.as_ref(); + let archetype_after_insert = self.archetype_after_insert.as_ref(); + let archetype = self.archetype.as_ref(); + + // SAFETY: All components in the bundle are guaranteed to exist in the World + // as they must be initialized before creating the BundleInfo. + unsafe { + // SAFETY: Mutable references do not alias and will be dropped after this block + let mut deferred_world = self.world.into_deferred(); + + if insert_mode == InsertMode::Replace { + if archetype.has_replace_observer() { + deferred_world.trigger_observers( + REPLACE, + Some(entity), + archetype_after_insert.iter_existing(), + caller, + ); + } + deferred_world.trigger_on_replace( + archetype, + entity, + archetype_after_insert.iter_existing(), + caller, + relationship_hook_mode, + ); + } + } + + let table = self.table.as_mut(); + + // SAFETY: Archetype gets borrowed when running the on_replace observers above, + // so this reference can only be promoted from shared to &mut down here, after they have been ran + let archetype = self.archetype.as_mut(); + + let (new_archetype, new_location, after_effect) = match &mut self.archetype_move_type { + ArchetypeMoveType::SameArchetype => { + // SAFETY: Mutable references do not alias and will be dropped after this block + let sparse_sets = { + let world = self.world.world_mut(); + &mut world.storages.sparse_sets + }; + + let after_effect = bundle_info.write_components( + table, + sparse_sets, + archetype_after_insert, + archetype_after_insert.required_components.iter(), + entity, + location.table_row, + self.change_tick, + bundle, + insert_mode, + caller, + ); + + (archetype, location, after_effect) + } + ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => { + let new_archetype = new_archetype.as_mut(); + + // SAFETY: Mutable references do not alias and will be dropped after this block + let (sparse_sets, entities) = { + let world = self.world.world_mut(); + (&mut world.storages.sparse_sets, &mut world.entities) + }; + + let result = archetype.swap_remove(location.archetype_row); + if let Some(swapped_entity) = result.swapped_entity { + let swapped_location = + // SAFETY: If the swap was successful, swapped_entity must be valid. + unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; + entities.set( + swapped_entity.index(), + Some(EntityLocation { + archetype_id: swapped_location.archetype_id, + archetype_row: location.archetype_row, + table_id: swapped_location.table_id, + table_row: swapped_location.table_row, + }), + ); + } + let new_location = new_archetype.allocate(entity, result.table_row); + entities.set(entity.index(), Some(new_location)); + let after_effect = bundle_info.write_components( + table, + sparse_sets, + archetype_after_insert, + archetype_after_insert.required_components.iter(), + entity, + result.table_row, + self.change_tick, + bundle, + insert_mode, + caller, + ); + + (new_archetype, new_location, after_effect) + } + ArchetypeMoveType::NewArchetypeNewTable { + new_archetype, + new_table, + } => { + let new_table = new_table.as_mut(); + let new_archetype = new_archetype.as_mut(); + + // SAFETY: Mutable references do not alias and will be dropped after this block + let (archetypes_ptr, sparse_sets, entities) = { + let world = self.world.world_mut(); + let archetype_ptr: *mut Archetype = world.archetypes.archetypes.as_mut_ptr(); + ( + archetype_ptr, + &mut world.storages.sparse_sets, + &mut world.entities, + ) + }; + let result = archetype.swap_remove(location.archetype_row); + if let Some(swapped_entity) = result.swapped_entity { + let swapped_location = + // SAFETY: If the swap was successful, swapped_entity must be valid. + unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; + entities.set( + swapped_entity.index(), + Some(EntityLocation { + archetype_id: swapped_location.archetype_id, + archetype_row: location.archetype_row, + table_id: swapped_location.table_id, + table_row: swapped_location.table_row, + }), + ); + } + // PERF: store "non bundle" components in edge, then just move those to avoid + // redundant copies + let move_result = table.move_to_superset_unchecked(result.table_row, new_table); + let new_location = new_archetype.allocate(entity, move_result.new_row); + entities.set(entity.index(), Some(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 = + // SAFETY: If the swap was successful, swapped_entity must be valid. + unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; + + entities.set( + swapped_entity.index(), + Some(EntityLocation { + archetype_id: swapped_location.archetype_id, + archetype_row: swapped_location.archetype_row, + table_id: swapped_location.table_id, + table_row: result.table_row, + }), + ); + + if archetype.id() == swapped_location.archetype_id { + archetype + .set_entity_table_row(swapped_location.archetype_row, result.table_row); + } else if new_archetype.id() == swapped_location.archetype_id { + new_archetype + .set_entity_table_row(swapped_location.archetype_row, result.table_row); + } else { + // SAFETY: the only two borrowed archetypes are above and we just did collision checks + (*archetypes_ptr.add(swapped_location.archetype_id.index())) + .set_entity_table_row(swapped_location.archetype_row, result.table_row); + } + } + + let after_effect = bundle_info.write_components( + new_table, + sparse_sets, + archetype_after_insert, + archetype_after_insert.required_components.iter(), + entity, + move_result.new_row, + self.change_tick, + bundle, + insert_mode, + caller, + ); + + (new_archetype, new_location, after_effect) + } + }; + + let new_archetype = &*new_archetype; + // SAFETY: We have no outstanding mutable references to world as they were dropped + let mut deferred_world = unsafe { self.world.into_deferred() }; + + // SAFETY: All components in the bundle are guaranteed to exist in the World + // as they must be initialized before creating the BundleInfo. + unsafe { + deferred_world.trigger_on_add( + new_archetype, + entity, + archetype_after_insert.iter_added(), + caller, + ); + if new_archetype.has_add_observer() { + deferred_world.trigger_observers( + ADD, + Some(entity), + archetype_after_insert.iter_added(), + caller, + ); + } + match insert_mode { + InsertMode::Replace => { + // Insert triggers for both new and existing components if we're replacing them. + deferred_world.trigger_on_insert( + new_archetype, + entity, + archetype_after_insert.iter_inserted(), + caller, + relationship_hook_mode, + ); + if new_archetype.has_insert_observer() { + deferred_world.trigger_observers( + INSERT, + Some(entity), + archetype_after_insert.iter_inserted(), + caller, + ); + } + } + InsertMode::Keep => { + // Insert triggers only for new components if we're not replacing them (since + // nothing is actually inserted). + deferred_world.trigger_on_insert( + new_archetype, + entity, + archetype_after_insert.iter_added(), + caller, + relationship_hook_mode, + ); + if new_archetype.has_insert_observer() { + deferred_world.trigger_observers( + INSERT, + Some(entity), + archetype_after_insert.iter_added(), + caller, + ); + } + } + } + } + + (new_location, after_effect) + } + + #[inline] + pub(crate) fn entities(&mut self) -> &mut Entities { + // SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers + unsafe { &mut self.world.world_mut().entities } + } +} + +impl BundleInfo { + /// Inserts a bundle into the given archetype and returns the resulting archetype and whether a new archetype was created. + /// This could be the same [`ArchetypeId`], in the event that inserting the given bundle + /// does not result in an [`Archetype`] change. + /// + /// Results are cached in the [`Archetype`] graph to avoid redundant work. + /// + /// # Safety + /// `components` must be the same components as passed in [`Self::new`] + pub(crate) unsafe fn insert_bundle_into_archetype( + &self, + archetypes: &mut Archetypes, + storages: &mut Storages, + components: &Components, + observers: &Observers, + archetype_id: ArchetypeId, + ) -> (ArchetypeId, bool) { + if let Some(archetype_after_insert_id) = archetypes[archetype_id] + .edges() + .get_archetype_after_bundle_insert(self.id) + { + return (archetype_after_insert_id, false); + } + let mut new_table_components = Vec::new(); + let mut new_sparse_set_components = Vec::new(); + let mut bundle_status = Vec::with_capacity(self.explicit_components_len); + let mut added_required_components = Vec::new(); + let mut added = Vec::new(); + let mut existing = Vec::new(); + + let current_archetype = &mut archetypes[archetype_id]; + for component_id in self.iter_explicit_components() { + if current_archetype.contains(component_id) { + bundle_status.push(ComponentStatus::Existing); + existing.push(component_id); + } else { + bundle_status.push(ComponentStatus::Added); + added.push(component_id); + // 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), + } + } + } + + for (index, component_id) in self.iter_required_components().enumerate() { + if !current_archetype.contains(component_id) { + added_required_components.push(self.required_components[index].clone()); + added.push(component_id); + // 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); + } + } + } + } + + 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 insert this bundle. + edges.cache_archetype_after_bundle_insert( + self.id, + archetype_id, + bundle_status, + added_required_components, + added, + existing, + ); + (archetype_id, false) + } else { + let table_id; + let table_components; + let sparse_set_components; + // The archetype changes when we insert 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_unstable(); + // 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_unstable(); + new_sparse_set_components + }; + }; + // SAFETY: ids in self must be valid + let (new_archetype_id, is_new_created) = archetypes.get_id_or_insert( + components, + observers, + table_id, + table_components, + sparse_set_components, + ); + + // Add an edge from the old archetype to the new archetype. + archetypes[archetype_id] + .edges_mut() + .cache_archetype_after_bundle_insert( + self.id, + new_archetype_id, + bundle_status, + added_required_components, + added, + existing, + ); + (new_archetype_id, is_new_created) + } + } +} diff --git a/crates/bevy_ecs/src/bundle/mod.rs b/crates/bevy_ecs/src/bundle/mod.rs index 31770fe117..5b9cefbdf0 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 insert; #[cfg(test)] mod tests; @@ -61,8 +62,8 @@ pub use bevy_ecs_macros::Bundle; use crate::{ archetype::{ - Archetype, ArchetypeAfterBundleInsert, ArchetypeCreated, ArchetypeId, Archetypes, - BundleComponentStatus, ComponentStatus, SpawnBundleStatus, + Archetype, ArchetypeCreated, ArchetypeId, Archetypes, BundleComponentStatus, + ComponentStatus, SpawnBundleStatus, }, change_detection::MaybeLocation, component::{ @@ -591,139 +592,6 @@ impl BundleInfo { } } - /// Inserts a bundle into the given archetype and returns the resulting archetype and whether a new archetype was created. - /// This could be the same [`ArchetypeId`], in the event that inserting the given bundle - /// does not result in an [`Archetype`] change. - /// - /// Results are cached in the [`Archetype`] graph to avoid redundant work. - /// - /// # Safety - /// `components` must be the same components as passed in [`Self::new`] - pub(crate) unsafe fn insert_bundle_into_archetype( - &self, - archetypes: &mut Archetypes, - storages: &mut Storages, - components: &Components, - observers: &Observers, - archetype_id: ArchetypeId, - ) -> (ArchetypeId, bool) { - if let Some(archetype_after_insert_id) = archetypes[archetype_id] - .edges() - .get_archetype_after_bundle_insert(self.id) - { - return (archetype_after_insert_id, false); - } - let mut new_table_components = Vec::new(); - let mut new_sparse_set_components = Vec::new(); - let mut bundle_status = Vec::with_capacity(self.explicit_components_len); - let mut added_required_components = Vec::new(); - let mut added = Vec::new(); - let mut existing = Vec::new(); - - let current_archetype = &mut archetypes[archetype_id]; - for component_id in self.iter_explicit_components() { - if current_archetype.contains(component_id) { - bundle_status.push(ComponentStatus::Existing); - existing.push(component_id); - } else { - bundle_status.push(ComponentStatus::Added); - added.push(component_id); - // 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), - } - } - } - - for (index, component_id) in self.iter_required_components().enumerate() { - if !current_archetype.contains(component_id) { - added_required_components.push(self.required_components[index].clone()); - added.push(component_id); - // 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); - } - } - } - } - - 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 insert this bundle. - edges.cache_archetype_after_bundle_insert( - self.id, - archetype_id, - bundle_status, - added_required_components, - added, - existing, - ); - (archetype_id, false) - } else { - let table_id; - let table_components; - let sparse_set_components; - // The archetype changes when we insert 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_unstable(); - // 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_unstable(); - new_sparse_set_components - }; - }; - // SAFETY: ids in self must be valid - let (new_archetype_id, is_new_created) = archetypes.get_id_or_insert( - components, - observers, - table_id, - table_components, - sparse_set_components, - ); - - // Add an edge from the old archetype to the new archetype. - archetypes[archetype_id] - .edges_mut() - .cache_archetype_after_bundle_insert( - self.id, - new_archetype_id, - bundle_status, - added_required_components, - added, - existing, - ); - (new_archetype_id, is_new_created) - } - } - /// Removes a bundle from the given archetype and returns the resulting archetype and whether a new archetype was created. /// (or `None` if the removal was invalid). /// This could be the same [`ArchetypeId`], in the event that removing the given bundle @@ -837,17 +705,6 @@ impl BundleInfo { } } -// SAFETY: We have exclusive world access so our pointers can't be invalidated externally -pub(crate) struct BundleInserter<'w> { - world: UnsafeWorldCell<'w>, - bundle_info: ConstNonNull, - archetype_after_insert: ConstNonNull, - table: NonNull
, - archetype: NonNull, - archetype_move_type: ArchetypeMoveType, - change_tick: Tick, -} - /// The type of archetype move (or lack thereof) that will result from a bundle /// being inserted into an entity. pub(crate) enum ArchetypeMoveType { @@ -865,384 +722,6 @@ pub(crate) enum ArchetypeMoveType { }, } -impl<'w> BundleInserter<'w> { - #[inline] - pub(crate) fn new( - world: &'w mut World, - archetype_id: ArchetypeId, - change_tick: Tick, - ) -> Self { - // SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too. - let mut registrator = - unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) }; - let bundle_id = world - .bundles - .register_info::(&mut registrator, &mut world.storages); - // SAFETY: We just ensured this bundle exists - unsafe { Self::new_with_id(world, archetype_id, bundle_id, change_tick) } - } - - /// Creates a new [`BundleInserter`]. - /// - /// # Safety - /// - Caller must ensure that `bundle_id` exists in `world.bundles`. - #[inline] - pub(crate) unsafe fn new_with_id( - world: &'w mut World, - archetype_id: ArchetypeId, - bundle_id: BundleId, - change_tick: Tick, - ) -> Self { - // SAFETY: We will not make any accesses to the command queue, component or resource data of this world - let bundle_info = world.bundles.get_unchecked(bundle_id); - let bundle_id = bundle_info.id(); - let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype( - &mut world.archetypes, - &mut world.storages, - &world.components, - &world.observers, - archetype_id, - ); - - let inserter = if new_archetype_id == archetype_id { - let archetype = &mut world.archetypes[archetype_id]; - // SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype - let archetype_after_insert = unsafe { - archetype - .edges() - .get_archetype_after_bundle_insert_internal(bundle_id) - .debug_checked_unwrap() - }; - let table_id = archetype.table_id(); - let table = &mut world.storages.tables[table_id]; - Self { - archetype_after_insert: archetype_after_insert.into(), - archetype: archetype.into(), - bundle_info: bundle_info.into(), - table: table.into(), - archetype_move_type: ArchetypeMoveType::SameArchetype, - change_tick, - world: world.as_unsafe_world_cell(), - } - } else { - let (archetype, new_archetype) = - world.archetypes.get_2_mut(archetype_id, new_archetype_id); - // SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype - let archetype_after_insert = unsafe { - archetype - .edges() - .get_archetype_after_bundle_insert_internal(bundle_id) - .debug_checked_unwrap() - }; - let table_id = archetype.table_id(); - let new_table_id = new_archetype.table_id(); - if table_id == new_table_id { - let table = &mut world.storages.tables[table_id]; - Self { - archetype_after_insert: archetype_after_insert.into(), - archetype: archetype.into(), - bundle_info: bundle_info.into(), - table: table.into(), - archetype_move_type: ArchetypeMoveType::NewArchetypeSameTable { - new_archetype: new_archetype.into(), - }, - change_tick, - world: world.as_unsafe_world_cell(), - } - } else { - let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id); - Self { - archetype_after_insert: archetype_after_insert.into(), - archetype: archetype.into(), - bundle_info: bundle_info.into(), - table: table.into(), - archetype_move_type: ArchetypeMoveType::NewArchetypeNewTable { - new_archetype: new_archetype.into(), - new_table: new_table.into(), - }, - change_tick, - world: world.as_unsafe_world_cell(), - } - } - }; - - if is_new_created { - inserter - .world - .into_deferred() - .trigger(ArchetypeCreated(new_archetype_id)); - } - inserter - } - - /// # Safety - /// `entity` must currently exist in the source archetype for this inserter. `location` - /// must be `entity`'s location in the archetype. `T` must match this [`BundleInfo`]'s type - #[inline] - pub(crate) unsafe fn insert( - &mut self, - entity: Entity, - location: EntityLocation, - bundle: T, - insert_mode: InsertMode, - caller: MaybeLocation, - relationship_hook_mode: RelationshipHookMode, - ) -> (EntityLocation, T::Effect) { - let bundle_info = self.bundle_info.as_ref(); - let archetype_after_insert = self.archetype_after_insert.as_ref(); - let archetype = self.archetype.as_ref(); - - // SAFETY: All components in the bundle are guaranteed to exist in the World - // as they must be initialized before creating the BundleInfo. - unsafe { - // SAFETY: Mutable references do not alias and will be dropped after this block - let mut deferred_world = self.world.into_deferred(); - - if insert_mode == InsertMode::Replace { - if archetype.has_replace_observer() { - deferred_world.trigger_observers( - REPLACE, - Some(entity), - archetype_after_insert.iter_existing(), - caller, - ); - } - deferred_world.trigger_on_replace( - archetype, - entity, - archetype_after_insert.iter_existing(), - caller, - relationship_hook_mode, - ); - } - } - - let table = self.table.as_mut(); - - // SAFETY: Archetype gets borrowed when running the on_replace observers above, - // so this reference can only be promoted from shared to &mut down here, after they have been ran - let archetype = self.archetype.as_mut(); - - let (new_archetype, new_location, after_effect) = match &mut self.archetype_move_type { - ArchetypeMoveType::SameArchetype => { - // SAFETY: Mutable references do not alias and will be dropped after this block - let sparse_sets = { - let world = self.world.world_mut(); - &mut world.storages.sparse_sets - }; - - let after_effect = bundle_info.write_components( - table, - sparse_sets, - archetype_after_insert, - archetype_after_insert.required_components.iter(), - entity, - location.table_row, - self.change_tick, - bundle, - insert_mode, - caller, - ); - - (archetype, location, after_effect) - } - ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => { - let new_archetype = new_archetype.as_mut(); - - // SAFETY: Mutable references do not alias and will be dropped after this block - let (sparse_sets, entities) = { - let world = self.world.world_mut(); - (&mut world.storages.sparse_sets, &mut world.entities) - }; - - let result = archetype.swap_remove(location.archetype_row); - if let Some(swapped_entity) = result.swapped_entity { - let swapped_location = - // SAFETY: If the swap was successful, swapped_entity must be valid. - unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; - entities.set( - swapped_entity.index(), - Some(EntityLocation { - archetype_id: swapped_location.archetype_id, - archetype_row: location.archetype_row, - table_id: swapped_location.table_id, - table_row: swapped_location.table_row, - }), - ); - } - let new_location = new_archetype.allocate(entity, result.table_row); - entities.set(entity.index(), Some(new_location)); - let after_effect = bundle_info.write_components( - table, - sparse_sets, - archetype_after_insert, - archetype_after_insert.required_components.iter(), - entity, - result.table_row, - self.change_tick, - bundle, - insert_mode, - caller, - ); - - (new_archetype, new_location, after_effect) - } - ArchetypeMoveType::NewArchetypeNewTable { - new_archetype, - new_table, - } => { - let new_table = new_table.as_mut(); - let new_archetype = new_archetype.as_mut(); - - // SAFETY: Mutable references do not alias and will be dropped after this block - let (archetypes_ptr, sparse_sets, entities) = { - let world = self.world.world_mut(); - let archetype_ptr: *mut Archetype = world.archetypes.archetypes.as_mut_ptr(); - ( - archetype_ptr, - &mut world.storages.sparse_sets, - &mut world.entities, - ) - }; - let result = archetype.swap_remove(location.archetype_row); - if let Some(swapped_entity) = result.swapped_entity { - let swapped_location = - // SAFETY: If the swap was successful, swapped_entity must be valid. - unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; - entities.set( - swapped_entity.index(), - Some(EntityLocation { - archetype_id: swapped_location.archetype_id, - archetype_row: location.archetype_row, - table_id: swapped_location.table_id, - table_row: swapped_location.table_row, - }), - ); - } - // PERF: store "non bundle" components in edge, then just move those to avoid - // redundant copies - let move_result = table.move_to_superset_unchecked(result.table_row, new_table); - let new_location = new_archetype.allocate(entity, move_result.new_row); - entities.set(entity.index(), Some(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 = - // SAFETY: If the swap was successful, swapped_entity must be valid. - unsafe { entities.get(swapped_entity).debug_checked_unwrap() }; - - entities.set( - swapped_entity.index(), - Some(EntityLocation { - archetype_id: swapped_location.archetype_id, - archetype_row: swapped_location.archetype_row, - table_id: swapped_location.table_id, - table_row: result.table_row, - }), - ); - - if archetype.id() == swapped_location.archetype_id { - archetype - .set_entity_table_row(swapped_location.archetype_row, result.table_row); - } else if new_archetype.id() == swapped_location.archetype_id { - new_archetype - .set_entity_table_row(swapped_location.archetype_row, result.table_row); - } else { - // SAFETY: the only two borrowed archetypes are above and we just did collision checks - (*archetypes_ptr.add(swapped_location.archetype_id.index())) - .set_entity_table_row(swapped_location.archetype_row, result.table_row); - } - } - - let after_effect = bundle_info.write_components( - new_table, - sparse_sets, - archetype_after_insert, - archetype_after_insert.required_components.iter(), - entity, - move_result.new_row, - self.change_tick, - bundle, - insert_mode, - caller, - ); - - (new_archetype, new_location, after_effect) - } - }; - - let new_archetype = &*new_archetype; - // SAFETY: We have no outstanding mutable references to world as they were dropped - let mut deferred_world = unsafe { self.world.into_deferred() }; - - // SAFETY: All components in the bundle are guaranteed to exist in the World - // as they must be initialized before creating the BundleInfo. - unsafe { - deferred_world.trigger_on_add( - new_archetype, - entity, - archetype_after_insert.iter_added(), - caller, - ); - if new_archetype.has_add_observer() { - deferred_world.trigger_observers( - ADD, - Some(entity), - archetype_after_insert.iter_added(), - caller, - ); - } - match insert_mode { - InsertMode::Replace => { - // Insert triggers for both new and existing components if we're replacing them. - deferred_world.trigger_on_insert( - new_archetype, - entity, - archetype_after_insert.iter_inserted(), - caller, - relationship_hook_mode, - ); - if new_archetype.has_insert_observer() { - deferred_world.trigger_observers( - INSERT, - Some(entity), - archetype_after_insert.iter_inserted(), - caller, - ); - } - } - InsertMode::Keep => { - // Insert triggers only for new components if we're not replacing them (since - // nothing is actually inserted). - deferred_world.trigger_on_insert( - new_archetype, - entity, - archetype_after_insert.iter_added(), - caller, - relationship_hook_mode, - ); - if new_archetype.has_insert_observer() { - deferred_world.trigger_observers( - INSERT, - Some(entity), - archetype_after_insert.iter_added(), - caller, - ); - } - } - } - } - - (new_location, after_effect) - } - - #[inline] - pub(crate) fn entities(&mut self) -> &mut Entities { - // SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers - unsafe { &mut self.world.world_mut().entities } - } -} - // SAFETY: We have exclusive world access so our pointers can't be invalidated externally pub(crate) struct BundleRemover<'w> { world: UnsafeWorldCell<'w>,