construct and destruct

This commit is contained in:
Elliott Pierce 2025-05-30 17:58:10 -04:00
parent 65a7ea9f6a
commit 4c6f613a1d
5 changed files with 265 additions and 148 deletions

View File

@ -1737,7 +1737,7 @@ impl<'w> BundleSpawner<'w> {
caller, caller,
); );
entities.set(entity.index(), Some(location)); entities.set(entity.index(), Some(location));
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick); entities.mark_construct_or_destruct(entity.index(), caller, self.change_tick);
(location, after_effect) (location, after_effect)
}; };

View File

@ -666,6 +666,13 @@ pub(crate) struct EntitiesAllocator {
} }
impl EntitiesAllocator { impl EntitiesAllocator {
/// Restarts the allocator.
pub(crate) fn restart(&mut self) {
self.free.clear();
*self.free_len.get_mut() = 0;
*self.next_row.get_mut() = 0;
}
pub(crate) fn free(&mut self, freed: Entity) { pub(crate) fn free(&mut self, freed: Entity) {
self.free.truncate(*self.free_len.get_mut() as usize); self.free.truncate(*self.free_len.get_mut() as usize);
self.free.push(freed); self.free.push(freed);
@ -752,6 +759,11 @@ impl Entities {
Self { meta: Vec::new() } Self { meta: Vec::new() }
} }
/// Clears all entity information
pub fn clear(&mut self) {
self.meta.clear()
}
/// Returns the [`EntityLocation`] of an [`Entity`]. /// Returns the [`EntityLocation`] of an [`Entity`].
/// Note: for pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`. /// Note: for pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`.
#[inline] #[inline]
@ -769,6 +781,25 @@ impl Entities {
.map(|meta| meta.location) .map(|meta| meta.location)
} }
/// Provides information regarding if `entity` may be constructed.
#[inline]
pub fn validate_construction(&self, entity: Entity) -> Result<(), ConstructionError> {
if self
.resolve_from_id(entity.row())
.is_some_and(|found| found.generation() != entity.generation())
{
Err(ConstructionError::InvalidId)
} else if self
.meta
.get(entity.index() as usize)
.is_some_and(|meta| meta.location.is_some())
{
Err(ConstructionError::AlreadyConstructed)
} else {
Ok(())
}
}
/// Updates the location of an [`EntityRow`]. /// Updates the location of an [`EntityRow`].
/// This must be called when moving the components of the existing entity around in storage. /// This must be called when moving the components of the existing entity around in storage.
/// ///
@ -791,6 +822,15 @@ impl Entities {
/// before handing control to unknown code. /// before handing control to unknown code.
#[inline] #[inline]
pub(crate) unsafe fn declare(&mut self, row: EntityRow, location: EntityIdLocation) { pub(crate) unsafe fn declare(&mut self, row: EntityRow, location: EntityIdLocation) {
self.ensure_row(row);
// SAFETY: We just did `ensure_row`
let meta = unsafe { self.meta.get_unchecked_mut(row.index() as usize) };
meta.location = location;
}
/// Ensures row is valid.
#[inline]
fn ensure_row(&mut self, row: EntityRow) {
#[cold] // to help with branch prediction #[cold] // to help with branch prediction
fn expand(meta: &mut Vec<EntityMeta>, len: usize) { fn expand(meta: &mut Vec<EntityMeta>, len: usize) {
meta.resize(len, EntityMeta::EMPTY); meta.resize(len, EntityMeta::EMPTY);
@ -801,44 +841,34 @@ impl Entities {
// TODO: hint unlikely once stable. // TODO: hint unlikely once stable.
expand(&mut self.meta, index + 1); expand(&mut self.meta, index + 1);
} }
// SAFETY: We guarantee that `index` a valid entity index
let meta = unsafe { self.meta.get_unchecked_mut(index) };
meta.location = location;
} }
/// Marks the entity as free if it exists, returning its [`EntityIdLocation`] and the [`Entity`] to reuse that [`EntityRow`]. /// Marks the `row` as free, returning the [`Entity`] to reuse that [`EntityRow`].
pub(crate) fn mark_free( ///
&mut self, /// # Safety
entity: Entity, ///
generations: u32, /// - `row` must its [`EntityIdLocation`] set to `None`.
) -> Option<(EntityIdLocation, Entity)> { pub(crate) unsafe fn mark_free(&mut self, row: EntityRow, generations: u32) -> Entity {
let meta = self.meta.get_mut(entity.index() as usize)?; // We need to do this in case an entity is being freed that was never constructed.
if meta.generation != entity.generation { self.ensure_row(row);
return None; // SAFETY: We just did `ensure_row`
} let meta = unsafe { self.meta.get_unchecked_mut(row.index() as usize) };
let (new_generation, aliased) = meta.generation.after_versions_and_could_alias(generations); let (new_generation, aliased) = meta.generation.after_versions_and_could_alias(generations);
meta.generation = new_generation; meta.generation = new_generation;
if aliased { if aliased {
warn!( warn!("EntityRow({row}) generation wrapped on Entities::free, aliasing may occur",);
"Entity({}) generation wrapped on Entities::free, aliasing may occur",
entity.row()
);
} }
let loc = meta.location.take(); Entity::from_raw_and_generation(row, meta.generation)
Some((
loc,
Entity::from_raw_and_generation(entity.row(), meta.generation),
))
} }
/// Mark an [`EntityRow`] as spawned or despawned in the given tick. /// Mark an [`EntityRow`] as constructed or destructed in the given tick.
/// ///
/// # Safety /// # Safety
/// - `row` must have a [`EntityIdLocation`]. /// - `row` must have either been constructed or destructed, ensuring its row is valid.
#[inline] #[inline]
pub(crate) unsafe fn mark_spawn_despawn( pub(crate) unsafe fn mark_construct_or_destruct(
&mut self, &mut self,
row: EntityRow, row: EntityRow,
by: MaybeLocation, by: MaybeLocation,
@ -948,6 +978,11 @@ impl Entities {
} }
} }
pub enum ConstructionError {
InvalidId,
AlreadyConstructed,
}
/// An error that occurs when a specified [`Entity`] does not exist. /// An error that occurs when a specified [`Entity`] does not exist.
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)] #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
#[error("The entity with ID {entity} {details}")] #[error("The entity with ID {entity} {details}")]

View File

@ -316,7 +316,9 @@ impl<'w, 's> Commands<'w, 's> {
let tick = world.change_tick(); let tick = world.change_tick();
// SAFETY: Entity has been flushed // SAFETY: Entity has been flushed
unsafe { unsafe {
world.entities_mut().mark_spawn_despawn(index, caller, tick); world
.entities_mut()
.mark_construct_or_destruct(index, caller, tick);
} }
}); });
entity_commands entity_commands
@ -382,7 +384,9 @@ impl<'w, 's> Commands<'w, 's> {
let tick = world.change_tick(); let tick = world.change_tick();
// SAFETY: Entity has been flushed // SAFETY: Entity has been flushed
unsafe { unsafe {
world.entities_mut().mark_spawn_despawn(index, caller, tick); world
.entities_mut()
.mark_construct_or_destruct(index, caller, tick);
} }
}); });

View File

@ -10,8 +10,8 @@ use crate::{
StorageType, Tick, StorageType, Tick,
}, },
entity::{ entity::{
ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent, ConstructionError, ContainsEntity, Entity, EntityCloner, EntityClonerBuilder,
EntityIdLocation, EntityLocation, EntityEquivalent, EntityIdLocation, EntityLocation,
}, },
event::Event, event::Event,
observer::Observer, observer::Observer,
@ -1169,9 +1169,8 @@ impl<'w> EntityWorldMut<'w> {
pub(crate) unsafe fn new( pub(crate) unsafe fn new(
world: &'w mut World, world: &'w mut World,
entity: Entity, entity: Entity,
location: Option<EntityLocation>, location: EntityIdLocation,
) -> Self { ) -> Self {
debug_assert!(world.entities().contains(entity));
debug_assert_eq!(world.entities().get(entity), location); debug_assert_eq!(world.entities().get(entity), location);
EntityWorldMut { EntityWorldMut {
@ -1223,6 +1222,12 @@ impl<'w> EntityWorldMut<'w> {
} }
} }
/// Returns whether or not the entity is constructed.
#[inline]
pub fn is_constructed(&self) -> bool {
self.location.is_some()
}
/// Returns the archetype that the current entity belongs to. /// Returns the archetype that the current entity belongs to.
/// ///
/// # Panics /// # Panics
@ -2328,35 +2333,39 @@ impl<'w> EntityWorldMut<'w> {
self self
} }
/// Despawns the current entity.
///
/// See [`World::despawn`] for more details.
///
/// # Note
///
/// This will also despawn any [`Children`](crate::hierarchy::Children) entities, and any other [`RelationshipTarget`](crate::relationship::RelationshipTarget) that is configured
/// to despawn descendants. This results in "recursive despawn" behavior.
///
/// # Panics
///
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
#[track_caller] #[track_caller]
pub fn despawn(self) { pub fn construct<B: Bundle>(&mut self, bundle: B) -> Result<&mut Self, ConstructionError> {
self.despawn_with_caller(MaybeLocation::caller()); let Self {
world,
entity,
location,
} = self;
let found = world.construct(*entity, bundle)?;
*location = found.location;
Ok(self)
} }
pub(crate) fn despawn_with_caller(self, caller: MaybeLocation) { #[track_caller]
let location = self.location(); pub fn destruct(&mut self) -> &mut Self {
let world = self.world; self.destruct_with_caller(MaybeLocation::caller())
let archetype = &world.archetypes[location.archetype_id]; }
pub(crate) fn destruct_with_caller(&mut self, caller: MaybeLocation) -> &mut Self {
// setup
let Some(location) = self.location else {
// If there is no location, we are already destructed
return self;
};
let archetype = &self.world.archetypes[location.archetype_id];
// SAFETY: Archetype cannot be mutably aliased by DeferredWorld // SAFETY: Archetype cannot be mutably aliased by DeferredWorld
let (archetype, mut deferred_world) = unsafe { let (archetype, mut deferred_world) = unsafe {
let archetype: *const Archetype = archetype; let archetype: *const Archetype = archetype;
let world = world.as_unsafe_world_cell(); let world = self.world.as_unsafe_world_cell();
(&*archetype, world.into_deferred()) (&*archetype, world.into_deferred())
}; };
// Triggers
// SAFETY: All components in the archetype exist in world // SAFETY: All components in the archetype exist in world
unsafe { unsafe {
if archetype.has_despawn_observer() { if archetype.has_despawn_observer() {
@ -2404,33 +2413,33 @@ impl<'w> EntityWorldMut<'w> {
); );
} }
// do the destruct
let change_tick = self.world.change_tick();
for component_id in archetype.components() { for component_id in archetype.components() {
world.removed_components.send(component_id, self.entity); self.world
.removed_components
.send(component_id, self.entity);
}
// SAFETY: Since we had a location, and it was valid, this is safe.
unsafe {
self.world.entities.update(self.entity.row(), None);
self.world
.entities
.mark_construct_or_destruct(self.entity.row(), caller, change_tick);
} }
// Observers and on_remove hooks may reserve new entities, which
// requires a flush before Entities::free may be called.
world.flush_entities();
let location = world
.entities
.free(self.entity)
.flatten()
.expect("entity should exist at this point.");
let table_row; let table_row;
let moved_entity; let moved_entity;
let change_tick = world.change_tick();
{ {
let archetype = &mut world.archetypes[location.archetype_id]; let archetype = &mut self.world.archetypes[location.archetype_id];
let remove_result = archetype.swap_remove(location.archetype_row); let remove_result = archetype.swap_remove(location.archetype_row);
if let Some(swapped_entity) = remove_result.swapped_entity { if let Some(swapped_entity) = remove_result.swapped_entity {
let swapped_location = world.entities.get(swapped_entity).unwrap(); let swapped_location = self.world.entities.get(swapped_entity).unwrap();
// SAFETY: swapped_entity is valid and the swapped entity's components are // SAFETY: swapped_entity is valid and the swapped entity's components are
// moved to the new location immediately after. // moved to the new location immediately after.
unsafe { unsafe {
world.entities.set( self.world.entities.update(
swapped_entity.index(), swapped_entity.row(),
Some(EntityLocation { Some(EntityLocation {
archetype_id: swapped_location.archetype_id, archetype_id: swapped_location.archetype_id,
archetype_row: location.archetype_row, archetype_row: location.archetype_row,
@ -2438,31 +2447,34 @@ impl<'w> EntityWorldMut<'w> {
table_row: swapped_location.table_row, table_row: swapped_location.table_row,
}), }),
); );
world
.entities
.mark_spawn_despawn(swapped_entity.index(), caller, change_tick);
} }
} }
table_row = remove_result.table_row; table_row = remove_result.table_row;
for component_id in archetype.sparse_set_components() { for component_id in archetype.sparse_set_components() {
// set must have existed for the component to be added. // set must have existed for the component to be added.
let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap(); let sparse_set = self
.world
.storages
.sparse_sets
.get_mut(component_id)
.unwrap();
sparse_set.remove(self.entity); sparse_set.remove(self.entity);
} }
// SAFETY: table rows stored in archetypes always exist // SAFETY: table rows stored in archetypes always exist
moved_entity = unsafe { moved_entity = unsafe {
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row) self.world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
}; };
}; };
// Handle displaced entity
if let Some(moved_entity) = moved_entity { if let Some(moved_entity) = moved_entity {
let moved_location = world.entities.get(moved_entity).unwrap(); let moved_location = self.world.entities.get(moved_entity).unwrap();
// SAFETY: `moved_entity` is valid and the provided `EntityLocation` accurately reflects // SAFETY: `moved_entity` is valid and the provided `EntityLocation` accurately reflects
// the current location of the entity and its component data. // the current location of the entity and its component data.
unsafe { unsafe {
world.entities.set( self.world.entities.update(
moved_entity.index(), moved_entity.row(),
Some(EntityLocation { Some(EntityLocation {
archetype_id: moved_location.archetype_id, archetype_id: moved_location.archetype_id,
archetype_row: moved_location.archetype_row, archetype_row: moved_location.archetype_row,
@ -2470,14 +2482,40 @@ impl<'w> EntityWorldMut<'w> {
table_row, table_row,
}), }),
); );
world
.entities
.mark_spawn_despawn(moved_entity.index(), caller, change_tick);
} }
world.archetypes[moved_location.archetype_id] self.world.archetypes[moved_location.archetype_id]
.set_entity_table_row(moved_location.archetype_row, table_row); .set_entity_table_row(moved_location.archetype_row, table_row);
} }
world.flush();
// finish
self.world.flush();
self
}
/// Despawns the current entity.
///
/// See [`World::despawn`] for more details.
///
/// # Note
///
/// This will also despawn any [`Children`](crate::hierarchy::Children) entities, and any other [`RelationshipTarget`](crate::relationship::RelationshipTarget) that is configured
/// to despawn descendants. This results in "recursive despawn" behavior.
///
/// # Panics
///
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
#[track_caller]
pub fn despawn(self) {
self.despawn_with_caller(MaybeLocation::caller());
}
pub(crate) fn despawn_with_caller(mut self, caller: MaybeLocation) {
self.destruct_with_caller(caller);
// SAFETY: We just destructed.
unsafe {
self.world
.release_generations_unchecked(self.entity.row(), 1);
}
} }
/// Ensures any commands triggered by the actions of Self are applied, equivalent to [`World::flush`] /// Ensures any commands triggered by the actions of Self are applied, equivalent to [`World::flush`]

View File

@ -14,11 +14,14 @@ pub mod unsafe_world_cell;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
pub mod reflect; pub mod reflect;
use crate::error::{DefaultErrorHandler, ErrorHandler};
pub use crate::{ pub use crate::{
change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD}, change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD},
world::command_queue::CommandQueue, world::command_queue::CommandQueue,
}; };
use crate::{
entity::{ConstructionError, EntitiesAllocator, EntityRow},
error::{DefaultErrorHandler, ErrorHandler},
};
pub use bevy_ecs_macros::FromWorld; pub use bevy_ecs_macros::FromWorld;
pub use component_constants::*; pub use component_constants::*;
pub use deferred_world::DeferredWorld; pub use deferred_world::DeferredWorld;
@ -89,6 +92,7 @@ use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
pub struct World { pub struct World {
id: WorldId, id: WorldId,
pub(crate) entities: Entities, pub(crate) entities: Entities,
pub(crate) allocator: EntitiesAllocator,
pub(crate) components: Components, pub(crate) components: Components,
pub(crate) component_ids: ComponentIds, pub(crate) component_ids: ComponentIds,
pub(crate) archetypes: Archetypes, pub(crate) archetypes: Archetypes,
@ -108,6 +112,7 @@ impl Default for World {
let mut world = Self { let mut world = Self {
id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"), id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"),
entities: Entities::new(), entities: Entities::new(),
allocator: EntitiesAllocator::default(),
components: Default::default(), components: Default::default(),
archetypes: Archetypes::new(), archetypes: Archetypes::new(),
storages: Default::default(), storages: Default::default(),
@ -1046,6 +1051,82 @@ impl World {
(fetcher, commands) (fetcher, commands)
} }
/// Spawns an [`Entity`] that is void/null.
/// The returned entity id is valid and unique, but it does not correspond to any conceptual entity.
/// The conceptual entity does not exist, and using the id as if it did may produce errors.
/// It can not be queried, and it has no [`EntityLocation`](crate::entity::EntityLocation).
///
/// This is different from empty entities, which do exist in the world;
/// they just happen to have no components.
pub fn spawn_null(&self) -> Entity {
self.allocator.alloc()
}
pub fn release(&mut self, entity: Entity) {
let Ok(entity) = self.get_entity_mut(entity) else {
return; // Already released then.
};
entity.despawn();
}
/// Releases `entity` to be reused.
///
/// # Safety
///
/// It must have been destructed.
pub(crate) unsafe fn release_generations_unchecked(
&mut self,
entity: EntityRow,
generations: u32,
) {
self.allocator
.free(self.entities.mark_free(entity, generations));
}
#[track_caller]
pub fn construct<B: Bundle>(
&mut self,
entity: Entity,
bundle: B,
) -> Result<EntityWorldMut<'_>, ConstructionError> {
self.entities.validate_construction(entity)?;
// SAFETY: We just ensured it was valid.
Ok(unsafe { self.construct_unchecked(entity, bundle, MaybeLocation::caller()) })
}
/// Constructs `bundle` on `entity`.
///
/// # Safety
///
/// `entity` must be valid and have no location.
pub(crate) unsafe fn construct_unchecked<B: Bundle>(
&mut self,
entity: Entity,
bundle: B,
caller: MaybeLocation,
) -> EntityWorldMut<'_> {
self.flush();
let change_tick = self.change_tick();
let mut bundle_spawner = BundleSpawner::new::<B>(self, change_tick);
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
let (entity_location, after_effect) =
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
let mut entity_location = Some(entity_location);
// SAFETY: command_queue is not referenced anywhere else
if !unsafe { self.command_queue.is_empty() } {
self.flush();
entity_location = self.entities().get(entity);
}
// SAFETY: entity and location are valid, as they were just created above
let mut entity = unsafe { EntityWorldMut::new(self, entity, entity_location) };
after_effect.apply(&mut entity);
entity
}
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used /// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
/// to add components to the entity or retrieve its id. /// to add components to the entity or retrieve its id.
/// ///
@ -1074,11 +1155,32 @@ impl World {
#[track_caller] #[track_caller]
pub fn spawn_empty(&mut self) -> EntityWorldMut { pub fn spawn_empty(&mut self) -> EntityWorldMut {
self.flush(); self.flush();
let entity = self.entities.alloc(); let entity = self.allocator.alloc();
// SAFETY: entity was just allocated // SAFETY: entity was just allocated
unsafe { self.spawn_at_empty_internal(entity, MaybeLocation::caller()) } unsafe { self.spawn_at_empty_internal(entity, MaybeLocation::caller()) }
} }
/// # Safety
/// must be called on an entity that was just allocated
unsafe fn spawn_at_empty_internal(
&mut self,
entity: Entity,
caller: MaybeLocation,
) -> EntityWorldMut {
let archetype = self.archetypes.empty_mut();
// PERF: consider avoiding allocating entities in the empty archetype unless needed
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
// empty
let location = unsafe { archetype.allocate(entity, table_row) };
let change_tick = self.change_tick();
self.entities.declare(entity.row(), Some(location));
self.entities
.mark_construct_or_destruct(entity.row(), caller, change_tick);
EntityWorldMut::new(self, entity, Some(location))
}
/// Spawns a new [`Entity`] with a given [`Bundle`] of [components](`Component`) and returns /// Spawns a new [`Entity`] with a given [`Bundle`] of [components](`Component`) and returns
/// a corresponding [`EntityWorldMut`], which can be used to add components to the entity or /// a corresponding [`EntityWorldMut`], which can be used to add components to the entity or
/// retrieve its id. In case large batches of entities need to be spawned, consider using /// retrieve its id. In case large batches of entities need to be spawned, consider using
@ -1149,47 +1251,9 @@ impl World {
bundle: B, bundle: B,
caller: MaybeLocation, caller: MaybeLocation,
) -> EntityWorldMut { ) -> EntityWorldMut {
self.flush(); let entity = self.spawn_null();
let change_tick = self.change_tick(); // SAFETY: This was just spawned from null.
let entity = self.entities.alloc(); unsafe { self.construct_unchecked(entity, bundle, caller) }
let mut bundle_spawner = BundleSpawner::new::<B>(self, change_tick);
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
let (entity_location, after_effect) =
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
let mut entity_location = Some(entity_location);
// SAFETY: command_queue is not referenced anywhere else
if !unsafe { self.command_queue.is_empty() } {
self.flush();
entity_location = self.entities().get(entity);
}
// SAFETY: entity and location are valid, as they were just created above
let mut entity = unsafe { EntityWorldMut::new(self, entity, entity_location) };
after_effect.apply(&mut entity);
entity
}
/// # Safety
/// must be called on an entity that was just allocated
unsafe fn spawn_at_empty_internal(
&mut self,
entity: Entity,
caller: MaybeLocation,
) -> EntityWorldMut {
let archetype = self.archetypes.empty_mut();
// PERF: consider avoiding allocating entities in the empty archetype unless needed
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
// empty
let location = unsafe { archetype.allocate(entity, table_row) };
let change_tick = self.change_tick();
self.entities.set(entity.index(), Some(location));
self.entities
.mark_spawn_despawn(entity.index(), caller, change_tick);
EntityWorldMut::new(self, entity, Some(location))
} }
/// Spawns a batch of entities with the same component [`Bundle`] type. Takes a given /// Spawns a batch of entities with the same component [`Bundle`] type. Takes a given
@ -2708,30 +2772,6 @@ impl World {
.initialize_with(component_id, &self.components) .initialize_with(component_id, &self.components)
} }
/// 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`].
#[track_caller]
pub(crate) fn flush_entities(&mut self) {
let by = MaybeLocation::caller();
let at = self.change_tick();
let empty_archetype = self.archetypes.empty_mut();
let table = &mut self.storages.tables[empty_archetype.table_id()];
// PERF: consider pre-allocating space for flushed entities
// SAFETY: entity is set to a valid location
unsafe {
self.entities.flush(
|entity, location| {
// SAFETY: no components are allocated by archetype.allocate() because the archetype
// is empty
*location = Some(empty_archetype.allocate(entity, table.allocate(entity)));
},
by,
at,
);
}
}
/// Applies any commands in the world's internal [`CommandQueue`]. /// Applies any commands in the world's internal [`CommandQueue`].
/// This does not apply commands from any systems, only those stored in the world. /// This does not apply commands from any systems, only those stored in the world.
/// ///
@ -2765,7 +2805,6 @@ impl World {
#[inline] #[inline]
#[track_caller] #[track_caller]
pub fn flush(&mut self) { pub fn flush(&mut self) {
self.flush_entities();
self.flush_components(); self.flush_components();
self.flush_commands(); self.flush_commands();
} }
@ -2980,6 +3019,7 @@ impl World {
self.storages.sparse_sets.clear_entities(); self.storages.sparse_sets.clear_entities();
self.archetypes.clear_entities(); self.archetypes.clear_entities();
self.entities.clear(); self.entities.clear();
self.allocator.restart();
} }
/// Clears all resources in this [`World`]. /// Clears all resources in this [`World`].