From ae2172eaf0d8140cebc0d136a86246b1f404e5c4 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 29 Oct 2022 11:13:47 +0200 Subject: [PATCH 01/15] move get_component[_with_type], get_ticks[_with_type], get_component_and_ticks[_with_type] to Storages --- crates/bevy_ecs/src/storage/mod.rs | 187 ++++++++++++ crates/bevy_ecs/src/world/entity_ref.rs | 381 ++++++++---------------- crates/bevy_ecs/src/world/mod.rs | 4 +- 3 files changed, 313 insertions(+), 259 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index b2fab3fdcb590..526155354a273 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -9,6 +9,14 @@ pub use resource::*; pub use sparse_set::*; pub use table::*; +use crate::{ + archetype::Archetypes, + component::{ComponentId, ComponentTicks, Components, StorageType, TickCells}, + entity::{Entity, EntityLocation}, +}; +use bevy_ptr::Ptr; +use std::any::TypeId; + /// The raw data stores of a [World](crate::world::World) #[derive(Default)] pub struct Storages { @@ -17,3 +25,182 @@ pub struct Storages { pub resources: Resources, pub non_send_resources: Resources, } + +impl Storages { + /// Get a raw pointer to a particular [`Component`] and its [`ComponentTicks`] identified by their [`TypeId`] + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_component_and_ticks_with_type( + &self, + archetypes: &Archetypes, + components: &Components, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + let component_id = components.get_id(type_id)?; + self.get_component_and_ticks(archetypes, component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`] and its [`ComponentTicks`] + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_component_and_ticks( + &self, + archetypes: &Archetypes, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + match storage_type { + StorageType::Table => { + let (components, table_row) = + fetch_table(archetypes, self, location, component_id)?; + + // SAFETY: archetypes only store valid table_rows and the stored component type is T + Some(( + components.get_data_unchecked(table_row), + TickCells { + added: components.get_added_ticks_unchecked(table_row), + changed: components.get_changed_ticks_unchecked(table_row), + }, + )) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_with_ticks(entity), + } + } + + /// Get a raw pointer to a particular [`Component`] on a particular [`Entity`], identified by the component's [`Type`] + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// the archetype + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_component_with_type( + &self, + archetypes: &Archetypes, + components: &Components, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + let component_id = components.get_id(type_id)?; + self.get_component(archetypes, component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`]. + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// the archetype + /// - `component_id` + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_component( + &self, + archetypes: &Archetypes, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + // SAFETY: component_id exists and is therefore valid + match storage_type { + StorageType::Table => { + let (components, table_row) = + fetch_table(archetypes, self, location, component_id)?; + // SAFETY: archetypes only store valid table_rows and the stored component type is T + Some(components.get_data_unchecked(table_row)) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get(entity), + } + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`] + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// the archetype + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_ticks_with_type( + &self, + archetypes: &Archetypes, + components: &Components, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + let component_id = components.get_id(type_id)?; + self.get_ticks(archetypes, component_id, storage_type, entity, location) + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`] + /// + /// # Safety + /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// the archetype + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `Archetypes` and `Components` must come from the world this of this `Storages` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub unsafe fn get_ticks( + &self, + archetypes: &Archetypes, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + match storage_type { + StorageType::Table => { + let (components, table_row) = + fetch_table(archetypes, self, location, component_id)?; + // SAFETY: archetypes only store valid table_rows and the stored component type is T + Some(components.get_ticks_unchecked(table_row)) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_ticks(entity), + } + } +} + +#[inline] +unsafe fn fetch_table<'s>( + archetypes: &Archetypes, + storages: &'s Storages, + location: EntityLocation, + component_id: ComponentId, +) -> Option<(&'s Column, TableRow)> { + let archetype = &archetypes[location.archetype_id]; + let table = &storages.tables[archetype.table_id()]; + let components = table.get_column(component_id)?; + let table_row = archetype.entity_table_row(location.archetype_row); + Some((components, table_row)) +} + +#[inline] +fn fetch_sparse_set(storages: &Storages, component_id: ComponentId) -> Option<&ComponentSparseSet> { + storages.sparse_sets.get(component_id) +} diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c2928aa93dd51..5d5783e45b344 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4,10 +4,9 @@ use crate::{ change_detection::{MutUntyped, TicksMut}, component::{ Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, - TickCells, }, entity::{Entities, Entity, EntityLocation}, - storage::{Column, ComponentSparseSet, SparseSet, Storages}, + storage::{SparseSet, Storages}, world::{Mut, World}, }; use bevy_ptr::{OwningPtr, Ptr}; @@ -72,17 +71,22 @@ impl<'w> EntityRef<'w> { pub fn get(&self) -> Option<&'w T> { // SAFETY: // - entity location and entity is valid - // - returned component is of type T + // - archetypes and components come from the same world // - the storage type provided is correct for T + // - world access is immutable, lifetime tied to `&self` unsafe { - get_component_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|value| value.deref::()) + self.world + .storages + .get_component_with_type( + &self.world.archetypes, + &self.world.components, + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + // SAFETY: returned component is of type T + .map(|value| value.deref::()) } } @@ -92,10 +96,13 @@ impl<'w> EntityRef<'w> { pub fn get_change_ticks(&self) -> Option { // SAFETY: // - entity location and entity is valid + // - archetypes and components come from the same world + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_ticks_with_type( - self.world, + self.world.storages.get_ticks_with_type( + &self.world.archetypes, + &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -112,15 +119,15 @@ impl<'w> EntityRef<'w> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - if !self.contains_id(component_id) { - return None; - } - let info = self.world.components().get_info(component_id)?; - // SAFETY: Entity location is valid and component_id exists. + // SAFETY: + // - entity location and entity is valid + // - archetypes and components come from the same world + // - world access is immutable, lifetime tied to `&self` + // - the storage type provided is correct for T unsafe { - get_ticks( - self.world, + self.world.storages.get_ticks( + &self.world.archetypes, component_id, info.storage_type(), self.entity, @@ -149,20 +156,23 @@ impl<'w> EntityRef<'w> { // - entity location and entity is valid // - returned component is of type T // - the storage type provided is correct for T - get_component_and_ticks_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|(value, ticks)| Mut { - // SAFETY: - // - returned component is of type T - // - Caller guarantees that this reference will not alias. - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), - }) + self.world + .storages + .get_component_and_ticks_with_type( + &self.world.archetypes, + &self.world.components, + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + // SAFETY: + // - returned component is of type T + // - Caller guarantees that this reference will not alias. + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), + }) } } @@ -179,12 +189,12 @@ impl<'w> EntityRef<'w> { pub fn get_by_id(&self, component_id: ComponentId) -> Option> { let info = self.world.components().get_info(component_id)?; // SAFETY: - // - entity_location is valid, - // - component_id is valid as checked by the line above + // - entity_location and entity are valid + // . component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self.world, + self.world.storages.get_component( + &self.world.archetypes, component_id, info.storage_type(), self.entity, @@ -257,19 +267,23 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get(&self) -> Option<&'_ T> { // SAFETY: - // - lifetimes enforce correct usage of returned borrow - // - entity location and entity is valid - // - returned component is of type T + // - entity location is valid + // - archetypes and components come from the same world + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_component_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|value| value.deref::()) + self.world + .storages + .get_component_with_type( + &self.world.archetypes, + &self.world.components, + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + // SAFETY: returned component is of type T + .map(|value| value.deref::()) } } @@ -284,11 +298,14 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get_change_ticks(&self) -> Option { // SAFETY: - // - entity location and entity is valid + // - entity location is valid + // - archetypes and components come from the same world + // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - get_ticks_with_type( - self.world, + self.world.storages.get_ticks_with_type( + &self.world.archetypes, + &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -305,15 +322,15 @@ impl<'w> EntityMut<'w> { /// compile time.** #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { - if !self.contains_id(component_id) { - return None; - } - let info = self.world.components().get_info(component_id)?; - // SAFETY: Entity location is valid and component_id exists. + // SAFETY: + // - entity location is valid + // - archetypes and components come from the same world + // - world access is immutable, lifetime tied to `&self` + // - the storage type provided is correct for T unsafe { - get_ticks( - self.world, + self.world.storages.get_ticks( + &self.world.archetypes, component_id, info.storage_type(), self.entity, @@ -338,21 +355,24 @@ impl<'w> EntityMut<'w> { // - entity location and entity is valid // - returned component is of type T // - the storage type provided is correct for T - get_component_and_ticks_with_type( - self.world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|(value, ticks)| Mut { - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells( - ticks, - self.world.last_change_tick(), - self.world.read_change_tick(), - ), - }) + self.world + .storages + .get_component_and_ticks_with_type( + &self.world.archetypes, + &self.world.components, + TypeId::of::(), + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, ticks)| Mut { + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells( + ticks, + self.world.last_change_tick(), + self.world.read_change_tick(), + ), + }) } /// Adds a [`Bundle`] of components to the entity. @@ -666,13 +686,14 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get_by_id(&self, component_id: ComponentId) -> Option> { let info = self.world.components().get_info(component_id)?; + self.world.components().get_info(component_id)?; // SAFETY: // - entity_location is valid // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self.world, + self.world.storages.get_component( + &self.world.archetypes, component_id, info.storage_type(), self.entity, @@ -697,100 +718,6 @@ impl<'w> EntityMut<'w> { } } -#[inline] -fn fetch_table( - world: &World, - location: EntityLocation, - component_id: ComponentId, -) -> Option<&Column> { - world.storages.tables[location.table_id].get_column(component_id) -} - -#[inline] -fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> { - world.storages.sparse_sets.get(component_id) -} - -// TODO: move to Storages? -/// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside -/// the archetype and table -/// - `component_id` must be valid -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -pub(crate) unsafe fn get_component( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option> { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(components.get_data_unchecked(location.table_row)) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get(entity), - } -} - -// TODO: move to Storages? -/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] on a particular [`Entity`] in the provided [World]. -/// -/// # Safety -/// - Caller must ensure that `component_id` is valid -/// - `location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_and_ticks( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option<(Ptr<'_>, TickCells<'_>)> { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(( - components.get_data_unchecked(location.table_row), - TickCells { - added: components.get_added_ticks_unchecked(location.table_row), - changed: components.get_changed_ticks_unchecked(location.table_row), - }, - )) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_with_ticks(entity), - } -} - -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `component_id` must be valid -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -unsafe fn get_ticks( - world: &World, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option { - match storage_type { - StorageType::Table => { - let components = fetch_table(world, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T - Some(components.get_ticks_unchecked(location.table_row)) - } - StorageType::SparseSet => fetch_sparse_set(world, component_id)?.get_ticks(entity), - } -} - // TODO: move to Storages? /// Moves component data out of storage. /// @@ -834,76 +761,6 @@ unsafe fn take_component<'a>( } } -/// Get a raw pointer to a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option> { - get_component( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - -/// Get a raw pointer to the [`ComponentTicks`] of a particular [`Component`] by [`TypeId`] on a particular [`Entity`] in the provided [`World`]. -/// -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_component_and_ticks_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option<(Ptr<'_>, TickCells<'_>)> { - get_component_and_ticks( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - -/// # Safety -/// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside -/// the archetype -/// - `type_id` must be correspond to a type that implements [`Component`] -/// - `storage_type` must accurately reflect where the components for `component_id` are stored. -#[inline] -unsafe fn get_ticks_with_type( - world: &World, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, -) -> Option { - get_ticks( - world, - world.components.get_id(type_id)?, - storage_type, - entity, - location, - ) -} - fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool { if let Some(component_id) = world.components.get_id(type_id) { contains_component_with_id(world, component_id, location) @@ -1045,20 +902,23 @@ pub(crate) unsafe fn get_mut( location: EntityLocation, ) -> Option> { // SAFETY: world access is unique, entity location is valid, and returned component is of type - // T + // T, storage type is correct let change_tick = world.change_tick(); let last_change_tick = world.last_change_tick(); - get_component_and_ticks_with_type( - world, - TypeId::of::(), - T::Storage::STORAGE_TYPE, - entity, - location, - ) - .map(|(value, ticks)| Mut { - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), - }) + world + .storages + .get_component_and_ticks_with_type( + &world.archetypes, + &world.components, + TypeId::of::(), + T::Storage::STORAGE_TYPE, + entity, + location, + ) + .map(|(value, ticks)| Mut { + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), + }) } // SAFETY: EntityLocation must be valid, component_id must be valid @@ -1071,13 +931,20 @@ pub(crate) unsafe fn get_mut_by_id( ) -> Option { let change_tick = world.change_tick(); let info = world.components.get_info_unchecked(component_id); - // SAFETY: world access is unique, entity location and component_id required to be valid - get_component_and_ticks(world, component_id, info.storage_type(), entity, location).map( - |(value, ticks)| MutUntyped { + // SAFETY: world access is unique, entity location and component_id required to be valid, storage_type is correct + world + .storages + .get_component_and_ticks( + &world.archetypes, + component_id, + info.storage_type(), + entity, + location, + ) + .map(|(value, ticks)| MutUntyped { value: value.assert_unique(), ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), - }, - ) + }) } #[cfg(test)] diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8cd32201d697b..0415ded01416d 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1727,8 +1727,8 @@ impl World { // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - get_component( - self, + self.storages.get_component( + &self.archetypes, component_id, info.storage_type(), entity, From 603ba07f8368823c00ce70b324c6baf11c31fc6c Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 29 Oct 2022 11:29:22 +0200 Subject: [PATCH 02/15] move take_component to Storages and add some safety comments --- crates/bevy_ecs/src/storage/mod.rs | 46 ++++++++++++++++++++- crates/bevy_ecs/src/world/entity_ref.rs | 55 ++++--------------------- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 526155354a273..d3fefaeb68989 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -14,7 +14,7 @@ use crate::{ component::{ComponentId, ComponentTicks, Components, StorageType, TickCells}, entity::{Entity, EntityLocation}, }; -use bevy_ptr::Ptr; +use bevy_ptr::{OwningPtr, Ptr}; use std::any::TypeId; /// The raw data stores of a [World](crate::world::World) @@ -186,6 +186,50 @@ impl Storages { } } +impl Storages { + /// Moves component data out of storage. + /// + /// This function leaves the underlying memory unchanged, but the component behind + /// returned pointer is semantically owned by the caller and will not be dropped in its original location. + /// Caller is responsible to drop component data behind returned pointer. + /// + /// # Safety + /// - `location` must be within bounds of the given archetype and `entity` must exist inside the `archetype` + /// - `component_id` must be valid + /// - `components` must come from the same world as `self` + /// - The relevant table row **must be removed** by the caller once all components are taken + #[inline] + pub(crate) unsafe fn take_component<'a>( + &'a mut self, + components: &Components, + removed_components: &mut SparseSet>, + component_id: ComponentId, + entity: Entity, + location: EntityLocation, + ) -> OwningPtr<'a> { + let component_info = components.get_info_unchecked(component_id); + let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); + removed_components.push(entity); + match component_info.storage_type() { + StorageType::Table => { + let table = &mut self.tables[location.table_id]; + // SAFETY: archetypes will always point to valid columns + let components = table.get_column_mut(component_id).unwrap(); + // SAFETY: archetypes only store valid table_rows and the stored component type is T + components + .get_data_unchecked_mut(location.table_row) + .promote() + } + StorageType::SparseSet => self + .sparse_sets + .get_mut(component_id) + .unwrap() + .remove_and_forget(entity) + .unwrap(), + } + } +} + #[inline] unsafe fn fetch_table<'s>( archetypes: &Archetypes, diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 5d5783e45b344..b12532c1682b0 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -6,10 +6,10 @@ use crate::{ Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, }, entity::{Entities, Entity, EntityLocation}, - storage::{SparseSet, Storages}, + storage::Storages, world::{Mut, World}, }; -use bevy_ptr::{OwningPtr, Ptr}; +use bevy_ptr::Ptr; use bevy_utils::tracing::debug; use std::any::TypeId; @@ -437,10 +437,12 @@ impl<'w> EntityMut<'w> { let result = unsafe { T::from_components(storages, &mut |storages| { let component_id = bundle_components.next().unwrap(); - // SAFETY: entity location is valid and table row is removed below - take_component( + // SAFETY: + // - entity location is valid + // - table row is removed below + // - `components` comes from the same world as `storages` + storages.take_component( components, - storages, removed_components, component_id, entity, @@ -718,49 +720,6 @@ impl<'w> EntityMut<'w> { } } -// TODO: move to Storages? -/// Moves component data out of storage. -/// -/// This function leaves the underlying memory unchanged, but the component behind -/// returned pointer is semantically owned by the caller and will not be dropped in its original location. -/// Caller is responsible to drop component data behind returned pointer. -/// -/// # Safety -/// - `location` must be within bounds of the given archetype and table and `entity` must exist inside the archetype -/// and table. -/// - `component_id` must be valid -/// - The relevant table row **must be removed** by the caller once all components are taken -#[inline] -unsafe fn take_component<'a>( - components: &Components, - storages: &'a mut Storages, - removed_components: &mut SparseSet>, - component_id: ComponentId, - entity: Entity, - location: EntityLocation, -) -> OwningPtr<'a> { - let component_info = components.get_info_unchecked(component_id); - let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); - removed_components.push(entity); - match component_info.storage_type() { - StorageType::Table => { - let table = &mut storages.tables[location.table_id]; - // SAFETY: archetypes will always point to valid columns - let components = table.get_column_mut(component_id).unwrap(); - // SAFETY: archetypes only store valid table_rows and the stored component type is T - components - .get_data_unchecked_mut(location.table_row) - .promote() - } - StorageType::SparseSet => storages - .sparse_sets - .get_mut(component_id) - .unwrap() - .remove_and_forget(entity) - .unwrap(), - } -} - fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool { if let Some(component_id) = world.components.get_id(type_id) { contains_component_with_id(world, component_id, location) From 053929d1e471f8ec61c099823c4e6bd97f3511a2 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 29 Oct 2022 11:37:32 +0200 Subject: [PATCH 03/15] don't use entity_ref::* import in world.rs to distinguish public and private imports, add safety comments --- crates/bevy_ecs/src/world/entity_ref.rs | 24 ++++++++++++++++++------ crates/bevy_ecs/src/world/mod.rs | 10 ++++++---- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index b12532c1682b0..d1fd83ea031da 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -855,15 +855,18 @@ fn sorted_remove(source: &mut Vec, remove: &[T]) { // SAFETY: EntityLocation must be valid #[inline] -pub(crate) unsafe fn get_mut( - world: &mut World, +pub(crate) unsafe fn get_mut<'w, T: Component>( + world: &'w mut World, entity: Entity, location: EntityLocation, ) -> Option> { - // SAFETY: world access is unique, entity location is valid, and returned component is of type - // T, storage type is correct let change_tick = world.change_tick(); let last_change_tick = world.last_change_tick(); + // SAFETY: + // - world access is unique + // - entity location is valid + // - and returned component is of type T + // - archetypes and components comes from the same world world .storages .get_component_and_ticks_with_type( @@ -875,6 +878,9 @@ pub(crate) unsafe fn get_mut( location, ) .map(|(value, ticks)| Mut { + // SAFETY: + // - world access is unique and ties world lifetime to `Mut` lifetime + // - `value` is of type `T` value: value.assert_unique().deref_mut::(), ticks: TicksMut::from_tick_cells(ticks, last_change_tick, change_tick), }) @@ -887,10 +893,15 @@ pub(crate) unsafe fn get_mut_by_id( entity: Entity, location: EntityLocation, component_id: ComponentId, -) -> Option { +) -> Option> { let change_tick = world.change_tick(); + // SAFETY: component_id is valid let info = world.components.get_info_unchecked(component_id); - // SAFETY: world access is unique, entity location and component_id required to be valid, storage_type is correct + // SAFETY: + // - world access is unique + // - entity location is valid + // - and returned component is of type T + // - archetypes and components comes from the same world world .storages .get_component_and_ticks( @@ -901,6 +912,7 @@ pub(crate) unsafe fn get_mut_by_id( location, ) .map(|(value, ticks)| MutUntyped { + // SAFETY: world access is unique and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), }) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 0415ded01416d..1a9be6239cc97 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3,7 +3,7 @@ mod spawn_batch; mod world_cell; pub use crate::change_detection::{Mut, Ref}; -pub use entity_ref::*; +pub use entity_ref::{EntityMut, EntityRef}; pub use spawn_batch::*; pub use world_cell::*; @@ -564,8 +564,10 @@ impl World { /// ``` #[inline] pub fn get_mut(&mut self, entity: Entity) -> Option> { - // SAFETY: lifetimes enforce correct usage of returned borrow - unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) } + // SAFETY: + // - lifetimes enforce correct usage of returned borrow + // - entity location is checked in `get_entity` + unsafe { entity_ref::get_mut(self, entity, self.get_entity(entity)?.location()) } } /// Despawns the given `entity`, if it exists. This will also remove all of the entity's @@ -1751,7 +1753,7 @@ impl World { self.components().get_info(component_id)?; // SAFETY: entity_location is valid, component_id is valid as checked by the line above unsafe { - get_mut_by_id( + entity_ref::get_mut_by_id( self, entity, self.get_entity(entity)?.location(), From 825970ffab19d7d512e3c52a0e42e190320c3ddd Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 29 Oct 2022 11:56:34 +0200 Subject: [PATCH 04/15] fix clippy --- crates/bevy_ecs/src/world/entity_ref.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index d1fd83ea031da..1004bab384e57 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -855,8 +855,8 @@ fn sorted_remove(source: &mut Vec, remove: &[T]) { // SAFETY: EntityLocation must be valid #[inline] -pub(crate) unsafe fn get_mut<'w, T: Component>( - world: &'w mut World, +pub(crate) unsafe fn get_mut( + world: &mut World, entity: Entity, location: EntityLocation, ) -> Option> { From 99aab8ddf633314d5544b680576ca503d3086450 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 29 Oct 2022 13:18:56 +0200 Subject: [PATCH 05/15] add doc links --- crates/bevy_ecs/src/storage/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index d3fefaeb68989..35bae43ad8c42 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -27,7 +27,7 @@ pub struct Storages { } impl Storages { - /// Get a raw pointer to a particular [`Component`] and its [`ComponentTicks`] identified by their [`TypeId`] + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`] /// /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside @@ -49,7 +49,7 @@ impl Storages { self.get_component_and_ticks(archetypes, component_id, storage_type, entity, location) } - /// Get a raw pointer to a particular [`Component`] and its [`ComponentTicks`] + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] /// /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside @@ -84,7 +84,7 @@ impl Storages { } } - /// Get a raw pointer to a particular [`Component`] on a particular [`Entity`], identified by the component's [`Type`] + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type /// /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside @@ -105,7 +105,7 @@ impl Storages { self.get_component(archetypes, component_id, storage_type, entity, location) } - /// Get a raw pointer to a particular [`Component`] on a particular [`Entity`] in the provided [`World`]. + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World). /// /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside From 0eff0a02ac3e6f239e5e3228c60b5505d8927da5 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Tue, 15 Nov 2022 20:22:22 +0100 Subject: [PATCH 06/15] remove unneccessary safety requirement --- crates/bevy_ecs/src/storage/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 35bae43ad8c42..5d03a4eac7d9b 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -31,7 +31,6 @@ impl Storages { /// /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside - /// - `component_id` must be valid /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated @@ -46,6 +45,7 @@ impl Storages { location: EntityLocation, ) -> Option<(Ptr<'_>, TickCells<'_>)> { let component_id = components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller self.get_component_and_ticks(archetypes, component_id, storage_type, entity, location) } @@ -89,6 +89,7 @@ impl Storages { /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] @@ -102,6 +103,7 @@ impl Storages { location: EntityLocation, ) -> Option> { let component_id = components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller self.get_component(archetypes, component_id, storage_type, entity, location) } @@ -140,6 +142,7 @@ impl Storages { /// # Safety /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] @@ -153,6 +156,7 @@ impl Storages { location: EntityLocation, ) -> Option { let component_id = components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller self.get_ticks(archetypes, component_id, storage_type, entity, location) } From 0b824b36bf17180c25cb4975ed109e1bc6bc4b39 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Tue, 15 Nov 2022 20:27:17 +0100 Subject: [PATCH 07/15] clean up entity_location comments --- crates/bevy_ecs/src/storage/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 5d03a4eac7d9b..2a195214ddec7 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -30,8 +30,8 @@ impl Storages { /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`] /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `location` must refer to an archetype that contains `entity` /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] @@ -52,7 +52,7 @@ impl Storages { /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `location` must refer to an archetype that contains `entity` /// - `component_id` must be valid /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` @@ -87,7 +87,7 @@ impl Storages { /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `location` must refer to an archetype that contains `entity` /// the archetype /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` @@ -110,7 +110,7 @@ impl Storages { /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World). /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `location` must refer to an archetype that contains `entity` /// the archetype /// - `component_id` /// - `storage_type` must accurately reflect where the components for `component_id` are stored. @@ -140,7 +140,7 @@ impl Storages { /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`] /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `location` must refer to an archetype that contains `entity` /// the archetype /// - `storage_type` must accurately reflect where the components for `component_id` are stored. /// - `Archetypes` and `Components` must come from the world this of this `Storages` @@ -163,7 +163,7 @@ impl Storages { /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`] /// /// # Safety - /// - `entity_location` must be within bounds of the given archetype and `entity` must exist inside + /// - `location` must refer to an archetype that contains `entity` /// the archetype /// - `component_id` must be valid /// - `storage_type` must accurately reflect where the components for `component_id` are stored. From a8405d3e12b3d13dc158696873ce3d45fecb20ac Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Tue, 15 Nov 2022 20:29:54 +0100 Subject: [PATCH 08/15] remove comments referring to type when untyped pointer is returned --- crates/bevy_ecs/src/storage/mod.rs | 13 ++++++++----- crates/bevy_ecs/src/world/entity_ref.rs | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 2a195214ddec7..06ca76612ee52 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -71,7 +71,7 @@ impl Storages { let (components, table_row) = fetch_table(archetypes, self, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(( components.get_data_unchecked(table_row), TickCells { @@ -130,7 +130,7 @@ impl Storages { StorageType::Table => { let (components, table_row) = fetch_table(archetypes, self, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(components.get_data_unchecked(table_row)) } StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get(entity), @@ -182,7 +182,7 @@ impl Storages { StorageType::Table => { let (components, table_row) = fetch_table(archetypes, self, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and the stored component type is T + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(components.get_ticks_unchecked(table_row)) } StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_ticks(entity), @@ -201,7 +201,7 @@ impl Storages { /// - `location` must be within bounds of the given archetype and `entity` must exist inside the `archetype` /// - `component_id` must be valid /// - `components` must come from the same world as `self` - /// - The relevant table row **must be removed** by the caller once all components are taken + /// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value #[inline] pub(crate) unsafe fn take_component<'a>( &'a mut self, @@ -219,7 +219,10 @@ impl Storages { let table = &mut self.tables[location.table_id]; // SAFETY: archetypes will always point to valid columns let components = table.get_column_mut(component_id).unwrap(); - // SAFETY: archetypes only store valid table_rows and the stored component type is T + // SAFETY: + // - archetypes only store valid table_rows + // - index is in bounds as promised by caller + // - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards components .get_data_unchecked_mut(location.table_row) .promote() diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1004bab384e57..69adf2c04419c 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -439,7 +439,7 @@ impl<'w> EntityMut<'w> { let component_id = bundle_components.next().unwrap(); // SAFETY: // - entity location is valid - // - table row is removed below + // - table row is removed below, without dropping the contents // - `components` comes from the same world as `storages` storages.take_component( components, From 579d053910ca374fb21d19ed24bd7f0585e08f54 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Fri, 9 Dec 2022 12:20:38 +0100 Subject: [PATCH 09/15] make methods pub(crate) --- crates/bevy_ecs/src/storage/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 06ca76612ee52..a2cc605d1dfb8 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -35,7 +35,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_component_and_ticks_with_type( + pub(crate) unsafe fn get_component_and_ticks_with_type( &self, archetypes: &Archetypes, components: &Components, @@ -58,7 +58,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_component_and_ticks( + pub(crate) unsafe fn get_component_and_ticks( &self, archetypes: &Archetypes, component_id: ComponentId, @@ -93,7 +93,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_component_with_type( + pub(crate) unsafe fn get_component_with_type( &self, archetypes: &Archetypes, components: &Components, @@ -117,7 +117,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_component( + pub(crate) unsafe fn get_component( &self, archetypes: &Archetypes, component_id: ComponentId, @@ -146,7 +146,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_ticks_with_type( + pub(crate) unsafe fn get_ticks_with_type( &self, archetypes: &Archetypes, components: &Components, @@ -170,7 +170,7 @@ impl Storages { /// - `Archetypes` and `Components` must come from the world this of this `Storages` /// - the caller must ensure that no aliasing rules are violated #[inline] - pub unsafe fn get_ticks( + pub(crate) unsafe fn get_ticks( &self, archetypes: &Archetypes, component_id: ComponentId, From 367b6304453f064c9315ab15b3db9e09c7e278f2 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Fri, 9 Dec 2022 12:29:22 +0100 Subject: [PATCH 10/15] move methods to World (except take_component) --- crates/bevy_ecs/src/storage/mod.rs | 238 ------------------------ crates/bevy_ecs/src/world/entity_ref.rs | 104 ++++++----- crates/bevy_ecs/src/world/mod.rs | 172 ++++++++++++++++- 3 files changed, 224 insertions(+), 290 deletions(-) diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index a2cc605d1dfb8..b2fab3fdcb590 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -9,14 +9,6 @@ pub use resource::*; pub use sparse_set::*; pub use table::*; -use crate::{ - archetype::Archetypes, - component::{ComponentId, ComponentTicks, Components, StorageType, TickCells}, - entity::{Entity, EntityLocation}, -}; -use bevy_ptr::{OwningPtr, Ptr}; -use std::any::TypeId; - /// The raw data stores of a [World](crate::world::World) #[derive(Default)] pub struct Storages { @@ -25,233 +17,3 @@ pub struct Storages { pub resources: Resources, pub non_send_resources: Resources, } - -impl Storages { - /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`] - /// - /// # Safety - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `location` must refer to an archetype that contains `entity` - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_component_and_ticks_with_type( - &self, - archetypes: &Archetypes, - components: &Components, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option<(Ptr<'_>, TickCells<'_>)> { - let component_id = components.get_id(type_id)?; - // SAFETY: component_id is valid, the rest is deferred to caller - self.get_component_and_ticks(archetypes, component_id, storage_type, entity, location) - } - - /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] - /// - /// # Safety - /// - `location` must refer to an archetype that contains `entity` - /// - `component_id` must be valid - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_component_and_ticks( - &self, - archetypes: &Archetypes, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option<(Ptr<'_>, TickCells<'_>)> { - match storage_type { - StorageType::Table => { - let (components, table_row) = - fetch_table(archetypes, self, location, component_id)?; - - // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules - Some(( - components.get_data_unchecked(table_row), - TickCells { - added: components.get_added_ticks_unchecked(table_row), - changed: components.get_changed_ticks_unchecked(table_row), - }, - )) - } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_with_ticks(entity), - } - } - - /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type - /// - /// # Safety - /// - `location` must refer to an archetype that contains `entity` - /// the archetype - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_component_with_type( - &self, - archetypes: &Archetypes, - components: &Components, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option> { - let component_id = components.get_id(type_id)?; - // SAFETY: component_id is valid, the rest is deferred to caller - self.get_component(archetypes, component_id, storage_type, entity, location) - } - - /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World). - /// - /// # Safety - /// - `location` must refer to an archetype that contains `entity` - /// the archetype - /// - `component_id` - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_component( - &self, - archetypes: &Archetypes, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option> { - // SAFETY: component_id exists and is therefore valid - match storage_type { - StorageType::Table => { - let (components, table_row) = - fetch_table(archetypes, self, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules - Some(components.get_data_unchecked(table_row)) - } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get(entity), - } - } - - /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`] - /// - /// # Safety - /// - `location` must refer to an archetype that contains `entity` - /// the archetype - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_ticks_with_type( - &self, - archetypes: &Archetypes, - components: &Components, - type_id: TypeId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option { - let component_id = components.get_id(type_id)?; - // SAFETY: component_id is valid, the rest is deferred to caller - self.get_ticks(archetypes, component_id, storage_type, entity, location) - } - - /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`] - /// - /// # Safety - /// - `location` must refer to an archetype that contains `entity` - /// the archetype - /// - `component_id` must be valid - /// - `storage_type` must accurately reflect where the components for `component_id` are stored. - /// - `Archetypes` and `Components` must come from the world this of this `Storages` - /// - the caller must ensure that no aliasing rules are violated - #[inline] - pub(crate) unsafe fn get_ticks( - &self, - archetypes: &Archetypes, - component_id: ComponentId, - storage_type: StorageType, - entity: Entity, - location: EntityLocation, - ) -> Option { - match storage_type { - StorageType::Table => { - let (components, table_row) = - fetch_table(archetypes, self, location, component_id)?; - // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules - Some(components.get_ticks_unchecked(table_row)) - } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_ticks(entity), - } - } -} - -impl Storages { - /// Moves component data out of storage. - /// - /// This function leaves the underlying memory unchanged, but the component behind - /// returned pointer is semantically owned by the caller and will not be dropped in its original location. - /// Caller is responsible to drop component data behind returned pointer. - /// - /// # Safety - /// - `location` must be within bounds of the given archetype and `entity` must exist inside the `archetype` - /// - `component_id` must be valid - /// - `components` must come from the same world as `self` - /// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value - #[inline] - pub(crate) unsafe fn take_component<'a>( - &'a mut self, - components: &Components, - removed_components: &mut SparseSet>, - component_id: ComponentId, - entity: Entity, - location: EntityLocation, - ) -> OwningPtr<'a> { - let component_info = components.get_info_unchecked(component_id); - let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); - removed_components.push(entity); - match component_info.storage_type() { - StorageType::Table => { - let table = &mut self.tables[location.table_id]; - // SAFETY: archetypes will always point to valid columns - let components = table.get_column_mut(component_id).unwrap(); - // SAFETY: - // - archetypes only store valid table_rows - // - index is in bounds as promised by caller - // - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards - components - .get_data_unchecked_mut(location.table_row) - .promote() - } - StorageType::SparseSet => self - .sparse_sets - .get_mut(component_id) - .unwrap() - .remove_and_forget(entity) - .unwrap(), - } - } -} - -#[inline] -unsafe fn fetch_table<'s>( - archetypes: &Archetypes, - storages: &'s Storages, - location: EntityLocation, - component_id: ComponentId, -) -> Option<(&'s Column, TableRow)> { - let archetype = &archetypes[location.archetype_id]; - let table = &storages.tables[archetype.table_id()]; - let components = table.get_column(component_id)?; - let table_row = archetype.entity_table_row(location.archetype_row); - Some((components, table_row)) -} - -#[inline] -fn fetch_sparse_set(storages: &Storages, component_id: ComponentId) -> Option<&ComponentSparseSet> { - storages.sparse_sets.get(component_id) -} diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 69adf2c04419c..9304143455025 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -6,10 +6,10 @@ use crate::{ Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, }, entity::{Entities, Entity, EntityLocation}, - storage::Storages, + storage::{SparseSet, Storages}, world::{Mut, World}, }; -use bevy_ptr::Ptr; +use bevy_ptr::{OwningPtr, Ptr}; use bevy_utils::tracing::debug; use std::any::TypeId; @@ -71,15 +71,11 @@ impl<'w> EntityRef<'w> { pub fn get(&self) -> Option<&'w T> { // SAFETY: // - entity location and entity is valid - // - archetypes and components come from the same world // - the storage type provided is correct for T // - world access is immutable, lifetime tied to `&self` unsafe { self.world - .storages .get_component_with_type( - &self.world.archetypes, - &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -96,13 +92,10 @@ impl<'w> EntityRef<'w> { pub fn get_change_ticks(&self) -> Option { // SAFETY: // - entity location and entity is valid - // - archetypes and components come from the same world // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - self.world.storages.get_ticks_with_type( - &self.world.archetypes, - &self.world.components, + self.world.get_ticks_with_type( TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -122,12 +115,10 @@ impl<'w> EntityRef<'w> { let info = self.world.components().get_info(component_id)?; // SAFETY: // - entity location and entity is valid - // - archetypes and components come from the same world // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - self.world.storages.get_ticks( - &self.world.archetypes, + self.world.get_ticks( component_id, info.storage_type(), self.entity, @@ -157,10 +148,7 @@ impl<'w> EntityRef<'w> { // - returned component is of type T // - the storage type provided is correct for T self.world - .storages .get_component_and_ticks_with_type( - &self.world.archetypes, - &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -193,8 +181,7 @@ impl<'w> EntityRef<'w> { // . component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - self.world.storages.get_component( - &self.world.archetypes, + self.world.get_component( component_id, info.storage_type(), self.entity, @@ -268,15 +255,11 @@ impl<'w> EntityMut<'w> { pub fn get(&self) -> Option<&'_ T> { // SAFETY: // - entity location is valid - // - archetypes and components come from the same world // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { self.world - .storages .get_component_with_type( - &self.world.archetypes, - &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -299,13 +282,10 @@ impl<'w> EntityMut<'w> { pub fn get_change_ticks(&self) -> Option { // SAFETY: // - entity location is valid - // - archetypes and components come from the same world // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - self.world.storages.get_ticks_with_type( - &self.world.archetypes, - &self.world.components, + self.world.get_ticks_with_type( TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -325,12 +305,10 @@ impl<'w> EntityMut<'w> { let info = self.world.components().get_info(component_id)?; // SAFETY: // - entity location is valid - // - archetypes and components come from the same world // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - self.world.storages.get_ticks( - &self.world.archetypes, + self.world.get_ticks( component_id, info.storage_type(), self.entity, @@ -356,10 +334,7 @@ impl<'w> EntityMut<'w> { // - returned component is of type T // - the storage type provided is correct for T self.world - .storages .get_component_and_ticks_with_type( - &self.world.archetypes, - &self.world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, self.entity, @@ -441,7 +416,8 @@ impl<'w> EntityMut<'w> { // - entity location is valid // - table row is removed below, without dropping the contents // - `components` comes from the same world as `storages` - storages.take_component( + take_component( + storages, components, removed_components, component_id, @@ -694,8 +670,7 @@ impl<'w> EntityMut<'w> { // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - self.world.storages.get_component( - &self.world.archetypes, + self.world.get_component( component_id, info.storage_type(), self.entity, @@ -866,12 +841,8 @@ pub(crate) unsafe fn get_mut( // - world access is unique // - entity location is valid // - and returned component is of type T - // - archetypes and components comes from the same world world - .storages .get_component_and_ticks_with_type( - &world.archetypes, - &world.components, TypeId::of::(), T::Storage::STORAGE_TYPE, entity, @@ -901,16 +872,8 @@ pub(crate) unsafe fn get_mut_by_id( // - world access is unique // - entity location is valid // - and returned component is of type T - // - archetypes and components comes from the same world world - .storages - .get_component_and_ticks( - &world.archetypes, - component_id, - info.storage_type(), - entity, - location, - ) + .get_component_and_ticks(component_id, info.storage_type(), entity, location) .map(|(value, ticks)| MutUntyped { // SAFETY: world access is unique and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), @@ -918,6 +881,51 @@ pub(crate) unsafe fn get_mut_by_id( }) } +/// Moves component data out of storage. +/// +/// This function leaves the underlying memory unchanged, but the component behind +/// returned pointer is semantically owned by the caller and will not be dropped in its original location. +/// Caller is responsible to drop component data behind returned pointer. +/// +/// # Safety +/// - `location` must be within bounds of the given archetype and `entity` must exist inside the `archetype` +/// - `component_id` must be valid +/// - `components` must come from the same world as `self` +/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value +#[inline] +pub(crate) unsafe fn take_component<'a>( + storages: &'a mut Storages, + components: &Components, + removed_components: &mut SparseSet>, + component_id: ComponentId, + entity: Entity, + location: EntityLocation, +) -> OwningPtr<'a> { + let component_info = components.get_info_unchecked(component_id); + let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); + removed_components.push(entity); + match component_info.storage_type() { + StorageType::Table => { + let table = &mut storages.tables[location.table_id]; + // SAFETY: archetypes will always point to valid columns + let components = table.get_column_mut(component_id).unwrap(); + // SAFETY: + // - archetypes only store valid table_rows + // - index is in bounds as promised by caller + // - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards + components + .get_data_unchecked_mut(location.table_row) + .promote() + } + StorageType::SparseSet => storages + .sparse_sets + .get_mut(component_id) + .unwrap() + .remove_and_forget(entity) + .unwrap(), + } +} + #[cfg(test)] mod tests { use crate as bevy_ecs; diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 1a9be6239cc97..577e2693c7c2c 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -12,13 +12,14 @@ use crate::{ bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, change_detection::{MutUntyped, TicksMut}, component::{ - Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, TickCells, + Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components, + StorageType, TickCells, }, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, event::{Event, Events}, ptr::UnsafeCellDeref, query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery}, - storage::{ResourceData, SparseSet, Storages}, + storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr}; @@ -1729,8 +1730,7 @@ impl World { // - component_id is valid as checked by the line above // - the storage type is accurate as checked by the fetched ComponentInfo unsafe { - self.storages.get_component( - &self.archetypes, + self.get_component( component_id, info.storage_type(), entity, @@ -1763,6 +1763,170 @@ impl World { } } +impl World { + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] identified by their [`TypeId`] + /// + /// # Safety + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - `location` must refer to an archetype that contains `entity` + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_and_ticks_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_component_and_ticks(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) and its [`ComponentTicks`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_and_ticks( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option<(Ptr<'_>, TickCells<'_>)> { + match storage_type { + StorageType::Table => { + let (components, table_row) = fetch_table(self, location, component_id)?; + + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(( + components.get_data_unchecked(table_row), + TickCells { + added: components.get_added_ticks_unchecked(table_row), + changed: components.get_changed_ticks_unchecked(table_row), + }, + )) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_with_ticks(entity), + } + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`], identified by the component's type + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_component(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to a particular [`Component`](crate::component::Component) on a particular [`Entity`] in the provided [`World`](crate::world::World). + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_component( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option> { + // SAFETY: component_id exists and is therefore valid + match storage_type { + StorageType::Table => { + let (components, table_row) = fetch_table(self, location, component_id)?; + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(components.get_data_unchecked(table_row)) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get(entity), + } + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`], identified by the component's [`TypeId`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_ticks_with_type( + &self, + type_id: TypeId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + let component_id = self.components.get_id(type_id)?; + // SAFETY: component_id is valid, the rest is deferred to caller + self.get_ticks(component_id, storage_type, entity, location) + } + + /// Get a raw pointer to the [`ComponentTicks`] on a particular [`Entity`] + /// + /// # Safety + /// - `location` must refer to an archetype that contains `entity` + /// the archetype + /// - `component_id` must be valid + /// - `storage_type` must accurately reflect where the components for `component_id` are stored. + /// - the caller must ensure that no aliasing rules are violated + #[inline] + pub(crate) unsafe fn get_ticks( + &self, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, + ) -> Option { + match storage_type { + StorageType::Table => { + let (components, table_row) = fetch_table(self, location, component_id)?; + // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules + Some(components.get_ticks_unchecked(table_row)) + } + StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_ticks(entity), + } + } +} + +#[inline] +unsafe fn fetch_table<'s>( + world: &World, + location: EntityLocation, + component_id: ComponentId, +) -> Option<(&Column, TableRow)> { + let archetype = &world.archetypes[location.archetype_id]; + let table = &world.storages.tables[archetype.table_id()]; + let components = table.get_column(component_id)?; + let table_row = archetype.entity_table_row(location.archetype_row); + Some((components, table_row)) +} + +#[inline] +fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> { + world.storages.sparse_sets.get(component_id) +} + impl fmt::Debug for World { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("World") From f8df2b683f67530acac70ba4d835507867253764 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Fri, 9 Dec 2022 12:32:42 +0100 Subject: [PATCH 11/15] remove unused lifetime --- crates/bevy_ecs/src/world/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 577e2693c7c2c..26a109a82973f 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1910,7 +1910,7 @@ impl World { } #[inline] -unsafe fn fetch_table<'s>( +unsafe fn fetch_table( world: &World, location: EntityLocation, component_id: ComponentId, From 8f5733ed4deada9648499af6752b534c7f03797e Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 17 Dec 2022 11:44:13 +0100 Subject: [PATCH 12/15] remove unnecessary get_info --- crates/bevy_ecs/src/world/entity_ref.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 9304143455025..b14e7c145b7cf 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -664,7 +664,6 @@ impl<'w> EntityMut<'w> { #[inline] pub fn get_by_id(&self, component_id: ComponentId) -> Option> { let info = self.world.components().get_info(component_id)?; - self.world.components().get_info(component_id)?; // SAFETY: // - entity_location is valid // - component_id is valid as checked by the line above From 0c483cfc8caa775235e6668633d3968ca4fcc149 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Sat, 17 Dec 2022 11:45:28 +0100 Subject: [PATCH 13/15] move fetch_table and fetch_sparse_set to World --- crates/bevy_ecs/src/world/mod.rs | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 26a109a82973f..720791b91259e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1800,7 +1800,7 @@ impl World { ) -> Option<(Ptr<'_>, TickCells<'_>)> { match storage_type { StorageType::Table => { - let (components, table_row) = fetch_table(self, location, component_id)?; + let (components, table_row) = self.fetch_table(location, component_id)?; // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(( @@ -1811,7 +1811,7 @@ impl World { }, )) } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_with_ticks(entity), + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_with_ticks(entity), } } @@ -1854,11 +1854,11 @@ impl World { // SAFETY: component_id exists and is therefore valid match storage_type { StorageType::Table => { - let (components, table_row) = fetch_table(self, location, component_id)?; + let (components, table_row) = self.fetch_table(location, component_id)?; // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(components.get_data_unchecked(table_row)) } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get(entity), + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get(entity), } } @@ -1900,31 +1900,31 @@ impl World { ) -> Option { match storage_type { StorageType::Table => { - let (components, table_row) = fetch_table(self, location, component_id)?; + let (components, table_row) = self.fetch_table(location, component_id)?; // SAFETY: archetypes only store valid table_rows and caller ensure aliasing rules Some(components.get_ticks_unchecked(table_row)) } - StorageType::SparseSet => fetch_sparse_set(self, component_id)?.get_ticks(entity), + StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_ticks(entity), } } -} -#[inline] -unsafe fn fetch_table( - world: &World, - location: EntityLocation, - component_id: ComponentId, -) -> Option<(&Column, TableRow)> { - let archetype = &world.archetypes[location.archetype_id]; - let table = &world.storages.tables[archetype.table_id()]; - let components = table.get_column(component_id)?; - let table_row = archetype.entity_table_row(location.archetype_row); - Some((components, table_row)) -} + #[inline] + unsafe fn fetch_table( + &self, + location: EntityLocation, + component_id: ComponentId, + ) -> Option<(&Column, TableRow)> { + let archetype = &self.archetypes[location.archetype_id]; + let table = &self.storages.tables[archetype.table_id()]; + let components = table.get_column(component_id)?; + let table_row = archetype.entity_table_row(location.archetype_row); + Some((components, table_row)) + } -#[inline] -fn fetch_sparse_set(world: &World, component_id: ComponentId) -> Option<&ComponentSparseSet> { - world.storages.sparse_sets.get(component_id) + #[inline] + fn fetch_sparse_set(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> { + self.storages.sparse_sets.get(component_id) + } } impl fmt::Debug for World { From 40bd78276184bb4d8b759a40f689feb2cf6bb73b Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Fri, 13 Jan 2023 14:48:16 +0100 Subject: [PATCH 14/15] fix safety comment --- crates/bevy_ecs/src/world/entity_ref.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index b14e7c145b7cf..f21675103fb34 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -887,7 +887,7 @@ pub(crate) unsafe fn get_mut_by_id( /// Caller is responsible to drop component data behind returned pointer. /// /// # Safety -/// - `location` must be within bounds of the given archetype and `entity` must exist inside the `archetype` +/// - `location.table_row` must be in bounds of column of component id `component_id` /// - `component_id` must be valid /// - `components` must come from the same world as `self` /// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value @@ -900,13 +900,13 @@ pub(crate) unsafe fn take_component<'a>( entity: Entity, location: EntityLocation, ) -> OwningPtr<'a> { + // SAFETY: caller promises component_id to be valid let component_info = components.get_info_unchecked(component_id); let removed_components = removed_components.get_or_insert_with(component_id, Vec::new); removed_components.push(entity); match component_info.storage_type() { StorageType::Table => { let table = &mut storages.tables[location.table_id]; - // SAFETY: archetypes will always point to valid columns let components = table.get_column_mut(component_id).unwrap(); // SAFETY: // - archetypes only store valid table_rows From 49e33a5103bf853cb4b087189845d5d24b13ffe1 Mon Sep 17 00:00:00 2001 From: Jakob Hellermann Date: Fri, 13 Jan 2023 14:49:13 +0100 Subject: [PATCH 15/15] make fetch_table safe --- crates/bevy_ecs/src/world/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 720791b91259e..2243af65deb6e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1909,7 +1909,7 @@ impl World { } #[inline] - unsafe fn fetch_table( + fn fetch_table( &self, location: EntityLocation, component_id: ComponentId,