diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 4dc3a85bdbf1b..bb3105af79a97 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -448,6 +448,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { change_tick: u32, ) -> Self::Item<'w, 's> { let (ptr, ticks) = world + .as_unsafe_world_cell_migration_internal() .get_resource_with_ticks(component_id) .unwrap_or_else(|| { panic!( @@ -488,6 +489,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { change_tick: u32, ) -> Self::Item<'w, 's> { world + .as_unsafe_world_cell_migration_internal() .get_resource_with_ticks(component_id) .map(|(ptr, ticks)| Res { value: ptr.deref(), @@ -542,7 +544,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { ) -> Self::Item<'w, 's> { let value = world .as_unsafe_world_cell_migration_internal() - .get_resource_mut_with_id(component_id) + .get_resource_mut_by_id(component_id) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -551,7 +553,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { ) }); ResMut { - value: value.value, + value: value.value.deref_mut::(), ticks: TicksMut { added: value.ticks.added, changed: value.ticks.changed, @@ -580,9 +582,9 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { ) -> Self::Item<'w, 's> { world .as_unsafe_world_cell_migration_internal() - .get_resource_mut_with_id(component_id) + .get_resource_mut_by_id(component_id) .map(|value| ResMut { - value: value.value, + value: value.value.deref_mut::(), ticks: TicksMut { added: value.ticks.added, changed: value.ticks.changed, @@ -974,6 +976,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { change_tick: u32, ) -> Self::Item<'w, 's> { let (ptr, ticks) = world + .as_unsafe_world_cell_migration_internal() .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { panic!( @@ -1012,6 +1015,7 @@ unsafe impl SystemParam for Option> { change_tick: u32, ) -> Self::Item<'w, 's> { world + .as_unsafe_world_cell_migration_internal() .get_non_send_with_ticks(component_id) .map(|(ptr, ticks)| NonSend { value: ptr.deref(), @@ -1064,6 +1068,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { change_tick: u32, ) -> Self::Item<'w, 's> { let (ptr, ticks) = world + .as_unsafe_world_cell_migration_internal() .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { panic!( @@ -1096,6 +1101,7 @@ unsafe impl<'a, T: 'static> SystemParam for Option> { change_tick: u32, ) -> Self::Item<'w, 's> { world + .as_unsafe_world_cell_migration_internal() .get_non_send_with_ticks(component_id) .map(|(ptr, ticks)| NonSendMut { value: ptr.assert_unique().deref_mut(), diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 7ab064500ff3a..0cbc597ef111a 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,10 +1,8 @@ use crate::{ archetype::{Archetype, ArchetypeId, Archetypes}, bundle::{Bundle, BundleInfo}, - change_detection::{MutUntyped, TicksMut}, - component::{ - Component, ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, - }, + change_detection::MutUntyped, + component::{Component, ComponentId, ComponentTicks, Components, StorageType}, entity::{Entities, Entity, EntityLocation}, storage::{SparseSet, Storages}, world::{Mut, World}, @@ -78,12 +76,22 @@ impl<'w> EntityRef<'w> { #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + // SAFETY: + // - `EntityRef` existing requires there not to be a `&mut World` + unsafe { + self.as_unsafe_world_cell_readonly() + .contains_id(component_id) + } } #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { - contains_component_with_type(self.world, type_id, self.location) + // SAFETY: + // - `EntityRef` existing requires there not to be a `&mut World` + unsafe { + self.as_unsafe_world_cell_readonly() + .contains_type_id(type_id) + } } #[inline] @@ -208,12 +216,22 @@ impl<'w> EntityMut<'w> { #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + // SAFETY: + // - `EntityMut` has exclusive access on world so noone else can have a `&mut World` + unsafe { + self.as_unsafe_world_cell_readonly() + .contains_id(component_id) + } } #[inline] pub fn contains_type_id(&self, type_id: TypeId) -> bool { - contains_component_with_type(self.world, type_id, self.location) + // SAFETY: + // - `EntityMut` has exclusive access on world so noone else can have a `&mut World` + unsafe { + self.as_unsafe_world_cell_readonly() + .contains_type_id(type_id) + } } #[inline] @@ -603,19 +621,10 @@ impl<'w> EntityMut<'w> { /// which is only valid while the [`EntityMut`] is alive. #[inline] 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 - // - the storage type is accurate as checked by the fetched ComponentInfo - unsafe { - self.world.get_component( - component_id, - info.storage_type(), - self.entity, - self.location, - ) - } + // - `&self` ensures that no mutable references exist to this entity's components. + // - `as_unsafe_world_cell_readonly` gives read only permission for all components on this entity + unsafe { self.as_unsafe_world_cell_readonly().get_by_id(component_id) } } /// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity. @@ -628,32 +637,13 @@ impl<'w> EntityMut<'w> { /// which is only valid while the [`EntityMut`] is alive. #[inline] pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option> { - self.world.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(self.world, self.entity, self.location, component_id) } - } -} - -pub(crate) 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) - } else { - false + // SAFETY: + // - `&mut self` ensures that no references exist to this entity's components. + // - `as_unsafe_world_cell` gives mutable permission for all components on this entity + unsafe { self.as_unsafe_world_cell().get_mut_by_id(component_id) } } } -pub(crate) fn contains_component_with_id( - world: &World, - component_id: ComponentId, - location: EntityLocation, -) -> bool { - world.archetypes[location.archetype_id].contains(component_id) -} - /// Removes a bundle from the given archetype and returns the resulting archetype (or None if the /// removal was invalid). in the event that adding the given bundle does not result in an Archetype /// change. Results are cached in the Archetype Graph to avoid redundant work. @@ -771,59 +761,6 @@ fn sorted_remove(source: &mut Vec, remove: &[T]) { }); } -// SAFETY: EntityLocation must be valid -#[inline] -pub(crate) unsafe fn get_mut( - world: &mut World, - entity: Entity, - location: EntityLocation, -) -> Option> { - let change_tick = world.change_tick(); - let last_change_tick = world.last_change_tick(); - // SAFETY: - // - world access is unique - // - entity location is valid - // - returned component is of type T - world - .get_component_and_ticks_with_type( - TypeId::of::(), - T::Storage::STORAGE_TYPE, - entity, - 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), - }) -} - -// SAFETY: EntityLocation must be valid, component_id must be valid -#[inline] -pub(crate) unsafe fn get_mut_by_id( - world: &mut World, - entity: Entity, - location: EntityLocation, - component_id: ComponentId, -) -> 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 is valid - // - returned component is of type T - world - .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(), - ticks: TicksMut::from_tick_cells(ticks, world.last_change_tick(), change_tick), - }) -} - /// Moves component data out of storage. /// /// This function leaves the underlying memory unchanged, but the component behind diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 87913d2b59ddb..fc863651c2b99 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -12,14 +12,11 @@ use crate::{ archetype::{ArchetypeComponentId, ArchetypeId, ArchetypeRow, Archetypes}, bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, change_detection::{MutUntyped, TicksMut}, - component::{ - Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components, - StorageType, TickCells, - }, + component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components}, entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation}, event::{Event, Events}, query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery}, - storage::{Column, ComponentSparseSet, ResourceData, SparseSet, Storages, TableRow}, + storage::{ResourceData, SparseSet, Storages}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr}; @@ -110,19 +107,19 @@ impl World { /// Creates a new [`UnsafeWorldCell`] view with complete read+write access. pub fn as_unsafe_world_cell(&mut self) -> UnsafeWorldCell<'_> { - UnsafeWorldCell::new(self) + UnsafeWorldCell::new_mutable(self) } /// Creates a new [`UnsafeWorldCell`] view with only read access to everything. pub fn as_unsafe_world_cell_readonly(&self) -> UnsafeWorldCell<'_> { - UnsafeWorldCell::new(self) + UnsafeWorldCell::new_readonly(self) } /// Creates a new [`UnsafeWorldCell`] with read+write access from a [&World](World). /// This is only a temporary measure until every `&World` that is semantically a [`UnsafeWorldCell`] /// has been replaced. pub(crate) fn as_unsafe_world_cell_migration_internal(&self) -> UnsafeWorldCell<'_> { - UnsafeWorldCell::new(self) + UnsafeWorldCell::new_readonly(self) } /// Retrieves this world's [Entities] collection @@ -594,9 +591,10 @@ impl World { #[inline] pub fn get_mut(&mut self, entity: Entity) -> Option> { // 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()) } + // - `as_unsafe_world_cell` is the only thing that is borrowing world + // - `as_unsafe_world_cell` provides mutable permission to everything + // - `&mut self` ensures no other borrows on world data + unsafe { self.as_unsafe_world_cell().get_entity(entity)?.get_mut() } } /// Despawns the given `entity`, if it exists. This will also remove all of the entity's @@ -1002,15 +1000,18 @@ impl World { /// Gets a reference to the resource of the given type if it exists #[inline] pub fn get_resource(&self) -> Option<&R> { - let component_id = self.components.get_resource_id(TypeId::of::())?; - // SAFETY: `component_id` was obtained from the type ID of `R`. - unsafe { self.get_resource_with_id(component_id) } + // SAFETY: + // - `as_unsafe_world_cell_readonly` gives permission to access everything immutably + // - `&self` ensures nothing in world is borrowed immutably + unsafe { self.as_unsafe_world_cell_readonly().get_resource() } } /// Gets a mutable reference to the resource of the given type if it exists #[inline] pub fn get_resource_mut(&mut self) -> Option> { - // SAFETY: unique world access + // SAFETY: + // - `as_unsafe_world_cell` gives permission to access everything mutably + // - `&mut self` ensures nothing in world is borrowed unsafe { self.as_unsafe_world_cell().get_resource_mut() } } @@ -1095,9 +1096,10 @@ impl World { /// This function will panic if it isn't called from the same thread that the resource was inserted from. #[inline] pub fn get_non_send_resource(&self) -> Option<&R> { - let component_id = self.components.get_resource_id(TypeId::of::())?; - // SAFETY: component id matches type T - unsafe { self.get_non_send_with_id(component_id) } + // SAFETY: + // - `as_unsafe_world_cell_readonly` gives permission to access the entire world immutably + // - `&self` ensures that there are no mutable borrows of world data + unsafe { self.as_unsafe_world_cell_readonly().get_non_send_resource() } } /// Gets a mutable reference to the non-send resource of the given type, if it exists. @@ -1107,34 +1109,12 @@ impl World { /// This function will panic if it isn't called from the same thread that the resource was inserted from. #[inline] pub fn get_non_send_resource_mut(&mut self) -> Option> { - // SAFETY: unique world access + // SAFETY: + // - `as_unsafe_world_cell` gives permission to access the entire world mutably + // - `&mut self` ensures that there are no borrows of world data unsafe { self.as_unsafe_world_cell().get_non_send_resource_mut() } } - // Shorthand helper function for getting the data and change ticks for a resource. - #[inline] - pub(crate) fn get_resource_with_ticks( - &self, - component_id: ComponentId, - ) -> Option<(Ptr<'_>, TickCells<'_>)> { - self.storages.resources.get(component_id)?.get_with_ticks() - } - - // Shorthand helper function for getting the data and change ticks for a resource. - /// - /// # Panics - /// This function will panic if it isn't called from the same thread that the resource was inserted from. - #[inline] - pub(crate) fn get_non_send_with_ticks( - &self, - component_id: ComponentId, - ) -> Option<(Ptr<'_>, TickCells<'_>)> { - self.storages - .non_send_resources - .get(component_id)? - .get_with_ticks() - } - // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. #[inline] pub(crate) fn get_resource_archetype_component_id( @@ -1377,36 +1357,6 @@ impl World { } } - /// # Safety - /// `component_id` must be assigned to a component of type `R` - #[inline] - pub(crate) unsafe fn get_resource_with_id( - &self, - component_id: ComponentId, - ) -> Option<&R> { - self.storages - .resources - .get(component_id)? - .get_data() - .map(|ptr| ptr.deref()) - } - - /// # Safety - /// `component_id` must be assigned to a component of type `R` - #[inline] - pub(crate) unsafe fn get_non_send_with_id( - &self, - component_id: ComponentId, - ) -> Option<&R> { - Some( - self.storages - .non_send_resources - .get(component_id)? - .get_data()? - .deref::(), - ) - } - /// Inserts a new resource with the given `value`. Will replace the value if it already existed. /// /// **You should prefer to use the typed API [`World::insert_resource`] where possible and only @@ -1613,7 +1563,13 @@ impl World { /// use this in cases where the actual types are not known at compile time.** #[inline] pub fn get_resource_by_id(&self, component_id: ComponentId) -> Option> { - self.storages.resources.get(component_id)?.get_data() + // SAFETY: + // - `as_unsafe_world_cell_readonly` gives permission to access the whole world immutably + // - `&self` ensures there are no mutable borrows on world data + unsafe { + self.as_unsafe_world_cell_readonly() + .get_resource_by_id(component_id) + } } /// Gets a pointer to the resource with the id [`ComponentId`] if it exists. @@ -1624,7 +1580,9 @@ impl World { /// use this in cases where the actual types are not known at compile time.** #[inline] pub fn get_resource_mut_by_id(&mut self, component_id: ComponentId) -> Option> { - // SAFETY: unique world access + // SAFETY: + // - `&mut self` ensures that all accessed data is unaliased + // - `as_unsafe_world_cell` provides mutable permission to the whole world unsafe { self.as_unsafe_world_cell() .get_resource_mut_by_id(component_id) @@ -1642,10 +1600,13 @@ impl World { /// This function will panic if it isn't called from the same thread that the resource was inserted from. #[inline] pub fn get_non_send_by_id(&self, component_id: ComponentId) -> Option> { - self.storages - .non_send_resources - .get(component_id)? - .get_data() + // SAFETY: + // - `as_unsafe_world_cell_readonly` gives permission to access the whole world immutably + // - `&self` ensures there are no mutable borrows on world data + unsafe { + self.as_unsafe_world_cell_readonly() + .get_non_send_resource_by_id(component_id) + } } /// Gets a `!Send` resource to the resource with the id [`ComponentId`] if it exists. @@ -1659,20 +1620,13 @@ impl World { /// This function will panic if it isn't called from the same thread that the resource was inserted from. #[inline] pub fn get_non_send_mut_by_id(&mut self, component_id: ComponentId) -> Option> { - let change_tick = self.change_tick(); - let (ptr, ticks) = self.get_non_send_with_ticks(component_id)?; - - let ticks = - // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. - // - index is in-bounds because the column is initialized and non-empty - // - no other reference to the ticks of the same row can exist at the same time - unsafe { TicksMut::from_tick_cells(ticks, self.last_change_tick(), change_tick) }; - - Some(MutUntyped { - // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`. - value: unsafe { ptr.assert_unique() }, - ticks, - }) + // SAFETY: + // - `&mut self` ensures that all accessed data is unaliased + // - `as_unsafe_world_cell` provides mutable permission to the whole world + unsafe { + self.as_unsafe_world_cell() + .get_non_send_resource_mut_by_id(component_id) + } } /// Removes the resource of a given type, if it exists. Otherwise returns [None]. @@ -1712,18 +1666,13 @@ impl World { /// This function will panic if it isn't called from the same thread that the resource was inserted from. #[inline] pub fn get_by_id(&self, entity: Entity, component_id: ComponentId) -> Option> { - let info = self.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 + // - `&self` ensures that all accessed data is not mutably aliased + // - `as_unsafe_world_cell_readonly` provides shared/readonly permission to the whole world unsafe { - self.get_component( - component_id, - info.storage_type(), - entity, - self.get_entity(entity)?.location(), - ) + self.as_unsafe_world_cell_readonly() + .get_entity(entity)? + .get_by_id(component_id) } } @@ -1738,181 +1687,15 @@ impl World { entity: Entity, component_id: ComponentId, ) -> Option> { - self.components().get_info(component_id)?; - // SAFETY: entity_location is valid, component_id is valid as checked by the line above + // SAFETY: + // - `&mut self` ensures that all accessed data is unaliased + // - `as_unsafe_world_cell` provides mutable permission to the whole world unsafe { - entity_ref::get_mut_by_id( - self, - entity, - self.get_entity(entity)?.location(), - component_id, - ) - } - } -} - -impl World { - /// Get an untyped 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 an untyped 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) = 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), - TickCells { - added: components.get_added_ticks_unchecked(table_row), - changed: components.get_changed_ticks_unchecked(table_row), - }, - )) - } - StorageType::SparseSet => self.fetch_sparse_set(component_id)?.get_with_ticks(entity), - } - } - - /// Get an untyped 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 an untyped 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) = 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 => self.fetch_sparse_set(component_id)?.get(entity), - } - } - - /// Get an untyped 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 an untyped 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) = 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 => self.fetch_sparse_set(component_id)?.get_ticks(entity), + self.as_unsafe_world_cell() + .get_entity(entity)? + .get_mut_by_id(component_id) } } - - #[inline] - 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(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> { - self.storages.sparse_sets.get(component_id) - } } impl fmt::Debug for World { diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 4f3539e75db39..77107e04dc251 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -1,18 +1,20 @@ #![warn(unsafe_op_in_unsafe_fn)] -use super::{entity_ref, Mut, World}; +use super::{Mut, World}; use crate::{ - archetype::{Archetype, Archetypes}, + archetype::{Archetype, ArchetypeComponentId, Archetypes}, bundle::Bundles, change_detection::{MutUntyped, TicksMut}, - component::{ComponentId, ComponentStorage, ComponentTicks, Components}, + component::{ + ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, TickCells, + }, entity::{Entities, Entity, EntityLocation}, prelude::Component, - storage::Storages, + storage::{Column, ComponentSparseSet, TableRow}, system::Resource, }; use bevy_ptr::Ptr; -use std::any::TypeId; +use std::{any::TypeId, cell::UnsafeCell, marker::PhantomData, sync::atomic::Ordering}; /// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid /// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule. @@ -70,75 +72,203 @@ use std::any::TypeId; /// } /// ``` #[derive(Copy, Clone)] -pub struct UnsafeWorldCell<'w>(&'w World); +pub struct UnsafeWorldCell<'w>(*mut World, PhantomData<(&'w World, &'w UnsafeCell)>); + +// SAFETY: `&World` and `&mut World` are both `Send` +unsafe impl Send for UnsafeWorldCell<'_> {} +// SAFETY: `&World` and `&mut World` are both `Sync` +unsafe impl Sync for UnsafeWorldCell<'_> {} impl<'w> UnsafeWorldCell<'w> { - pub(crate) fn new(world: &'w World) -> Self { - UnsafeWorldCell(world) + pub(crate) fn new_readonly(world: &'w World) -> Self { + UnsafeWorldCell(world as *const World as *mut World, PhantomData) } - /// Gets a reference to the [`&World`](crate::world::World) this [`UnsafeWorldCell`] belongs to. - /// This can be used to call methods like [`World::contains_resource`] which aren't exposed and but don't perform any accesses. + pub(crate) fn new_mutable(world: &'w mut World) -> Self { + // SAFETY: raw pointer is derived from an `&mut World` and `Unsafecell` has + // the same layout as `World`. + unsafe { + let world: &'w UnsafeCell = &*(world as *mut World as *mut UnsafeCell); + Self(world as *const UnsafeCell as *mut World, PhantomData) + } + } + + /// This `&mut World` counts as a mutable borrow of every resource and component for the purposes + /// of safety invariants that talk about borrows on component/resource data not existing. This is + /// true even for methods that do not explicitly call out `&mut World` as problematic. /// - /// **Note**: You *must not* hand out a `&World` reference to arbitrary safe code when the [`UnsafeWorldCell`] is currently - /// being used for mutable accesses. + /// # Safety + /// - there must be no borrows on world + pub unsafe fn world_mut(self) -> &'w mut World { + // SAFETY: + // - caller ensures the created `&mut World` is the only borrow of world + unsafe { &mut *self.0 } + } + + /// Gets a reference to the [`&World`](crate::world::World) this [`UnsafeWorldCell`] belongs to. + /// This can be used for arbitrary shared/readonly access. /// /// # Safety - /// - the world must not be used to access any resources or components. You can use it to safely access metadata. + /// - there must be no live exclusive borrows on world data + /// - there must be no live exclusive borrow of world pub unsafe fn world(self) -> &'w World { - self.0 + // SAFETY: + // - caller ensures there is no `&mut World` this makes it okay to make a `&World` + // - caller ensures there is no mutable borrows of world data, this means the caller cannot + // misuse the returned `&World` + unsafe { &*self.0 } + } + + /// Variant on [`UnsafeWorldCell::world`] solely used for implementing this type's methods. + /// It allows having an `&World` even with live mutable borrows of components and resources + /// so the returned `&World` should not be handed out to safe code and care should be taken + /// when working with it. + /// + /// Deliberately private as the correct way to access data in a [`World`] that may have existing + /// mutable borrows of data inside it, is to use [`UnsafeWorldCell`]. + /// + /// # Safety + /// - there must be no live exclusive borrow of the world + /// - must not be used in a way that would conflcit with any + /// live exclusive borrows on world data + unsafe fn unsafe_world(self) -> &'w World { + // SAFETY: + // - caller ensures there is no `&mut World` this makes it okay to make a `&World` + // - caller ensures that the returned `&World` is not used in a way that would conflict + // with any existing mutable borrows of world data + unsafe { &*self.0 } } /// Retrieves this world's [Entities] collection + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn entities(self) -> &'w Entities { - &self.0.entities + pub unsafe fn entities(self) -> &'w Entities { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + &unsafe { self.unsafe_world() }.entities } /// Retrieves this world's [Archetypes] collection + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn archetypes(self) -> &'w Archetypes { - &self.0.archetypes + pub unsafe fn archetypes(self) -> &'w Archetypes { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + &unsafe { self.unsafe_world() }.archetypes } /// Retrieves this world's [Components] collection + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn components(self) -> &'w Components { - &self.0.components + pub unsafe fn components(self) -> &'w Components { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + &unsafe { self.unsafe_world() }.components } - /// Retrieves this world's [Storages] collection + /// Retrieves this world's [Bundles] collection + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn storages(self) -> &'w Storages { - &self.0.storages + pub unsafe fn bundles(self) -> &'w Bundles { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + &unsafe { self.unsafe_world() }.bundles } - /// Retrieves this world's [Bundles] collection + /// Reads the current change tick of this world. + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn bundles(self) -> &'w Bundles { - &self.0.bundles + pub unsafe fn read_change_tick(self) -> u32 { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + unsafe { self.unsafe_world() } + .change_tick + .load(Ordering::Acquire) } - /// Reads the current change tick of this world. + /// # Safety + /// - there must not be an `&mut World` + #[inline] + pub unsafe fn last_change_tick(self) -> u32 { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + unsafe { self.unsafe_world() }.last_change_tick + } + + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn read_change_tick(self) -> u32 { - self.0.read_change_tick() + pub unsafe fn increment_change_tick(self) -> u32 { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + unsafe { self.unsafe_world() } + .change_tick + .fetch_add(1, Ordering::AcqRel) } + /// Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn last_change_tick(self) -> u32 { - self.0.last_change_tick() + pub unsafe fn get_resource_archetype_component_id( + self, + component_id: ComponentId, + ) -> Option { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + let resource = unsafe { self.unsafe_world() } + .storages + .resources + .get(component_id)?; + Some(resource.id()) } + /// Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. + /// + /// # Safety + /// - there must not be an `&mut World` #[inline] - pub fn increment_change_tick(self) -> u32 { - self.0.increment_change_tick() + pub unsafe fn get_non_send_archetype_component_id( + self, + component_id: ComponentId, + ) -> Option { + // SAFETY: + // - caller ensures there is no `&mut World` + // - we only access world metadata + let resource = unsafe { self.unsafe_world() } + .storages + .non_send_resources + .get(component_id)?; + Some(resource.id()) } /// Retrieves an [`UnsafeWorldCellEntityRef`] that exposes read and write operations for the given `entity`. /// Similar to the [`UnsafeWorldCell`], you are in charge of making sure that no aliasing rules are violated. - pub fn get_entity(self, entity: Entity) -> Option> { - let location = self.0.entities.get(entity)?; + /// + /// # Safety + /// - There must be no live exclusive borrow of world + pub unsafe fn get_entity(self, entity: Entity) -> Option> { + // SAFETY: caller ensures there is no `&mut World` + let location = unsafe { self.entities() }.get(entity)?; Some(UnsafeWorldCellEntityRef::new(self, entity, location)) } @@ -148,9 +278,18 @@ impl<'w> UnsafeWorldCell<'w> { /// It is the callers responsibility to ensure that /// - the [`UnsafeWorldCell`] has permission to access the resource /// - no mutable reference to the resource exists at the same time + /// - no live exclusive borrow of world #[inline] pub unsafe fn get_resource(self) -> Option<&'w R> { - self.0.get_resource::() + // SAFETY: caller ensures that there is no `&mut World` which means we can access world metadata + let component_id = unsafe { self.components() }.get_resource_id(TypeId::of::())?; + // SAFETY: caller ensures `self` has permission to access the resource + // caller also ensure that no mutable reference to the resource exists + unsafe { + self.get_resource_by_id(component_id) + // SAFETY: `component_id` was obtained from the type ID of `R`. + .map(|ptr| ptr.deref::()) + } } /// Gets a pointer to the resource with the id [`ComponentId`] if it exists. @@ -166,7 +305,13 @@ impl<'w> UnsafeWorldCell<'w> { /// - no mutable reference to the resource exists at the same time #[inline] pub unsafe fn get_resource_by_id(self, component_id: ComponentId) -> Option> { - self.0.get_resource_by_id(component_id) + // SAFETY: caller ensures that `self` has permission to access `R` + // caller ensures that no mutable reference exists to `R` + unsafe { self.unsafe_world() } + .storages + .resources + .get(component_id)? + .get_data() } /// Gets a reference to the non-send resource of the given type if it exists @@ -175,9 +320,18 @@ impl<'w> UnsafeWorldCell<'w> { /// It is the callers responsibility to ensure that /// - the [`UnsafeWorldCell`] has permission to access the resource /// - no mutable reference to the resource exists at the same time + /// - no live exclusive borrow of world #[inline] pub unsafe fn get_non_send_resource(self) -> Option<&'w R> { - self.0.get_non_send_resource() + // SAFETY: caller ensures there is no `&mut World` which means we can access world metadata + let component_id = unsafe { self.components() }.get_resource_id(TypeId::of::())?; + // SAFETY: caller ensures that `self` has permission to access `R` + // caller ensures that no mutable reference exists to `R` + unsafe { + self.get_non_send_resource_by_id(component_id) + // SAEFTY: `component_id` was obtained from `TypeId::of::()` + .map(|ptr| ptr.deref::()) + } } /// Gets a `!Send` resource to the resource with the id [`ComponentId`] if it exists. @@ -196,7 +350,9 @@ impl<'w> UnsafeWorldCell<'w> { /// - no mutable reference to the resource exists at the same time #[inline] pub unsafe fn get_non_send_resource_by_id(self, component_id: ComponentId) -> Option> { - self.0 + // SAFETY: we only access data on world that the caller has ensured is unaliased and we have + // permission to access. + unsafe { self.unsafe_world() } .storages .non_send_resources .get(component_id)? @@ -209,14 +365,19 @@ impl<'w> UnsafeWorldCell<'w> { /// It is the callers responsibility to ensure that /// - the [`UnsafeWorldCell`] has permission to access the resource mutably /// - no other references to the resource exist at the same time + /// - no live exclusive borrow of the world #[inline] pub unsafe fn get_resource_mut(self) -> Option> { - let component_id = self.0.components.get_resource_id(TypeId::of::())?; + // SAFETY: caller ensures there is no `&mut World` which means we can access world metadata. + let component_id = unsafe { self.components() }.get_resource_id(TypeId::of::())?; // SAFETY: - // - component_id is of type `R` - // - caller ensures aliasing rules - // - `R` is Send + Sync - unsafe { self.get_resource_mut_with_id(component_id) } + // - caller ensures `self` has permission to access the resource mutably + // - caller ensures no other references to the resource exist + unsafe { + self.get_resource_mut_by_id(component_id) + // `component_id` was gotten from `TypeId::of::()` + .map(|ptr| ptr.with_type::()) + } } /// Gets a pointer to the resource with the id [`ComponentId`] if it exists. @@ -232,10 +393,16 @@ impl<'w> UnsafeWorldCell<'w> { /// - no other references to the resource exist at the same time #[inline] pub unsafe fn get_resource_mut_by_id( - &self, + self, component_id: ComponentId, ) -> Option> { - let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?; + // SAFETY: we only access data that the caller has ensured is unaliased and `self` + // has permission to access. + let (ptr, ticks) = unsafe { self.unsafe_world() } + .storages + .resources + .get(component_id)? + .get_with_ticks()?; // SAFETY: // - index is in-bounds because the column is initialized and non-empty @@ -257,11 +424,19 @@ impl<'w> UnsafeWorldCell<'w> { /// It is the callers responsibility to ensure that /// - the [`UnsafeWorldCell`] has permission to access the resource mutably /// - no other references to the resource exist at the same time + /// - no live exclusive borrow of the world #[inline] pub unsafe fn get_non_send_resource_mut(self) -> Option> { - let component_id = self.0.components.get_resource_id(TypeId::of::())?; - // SAFETY: component_id matches `R`, rest is promised by caller - unsafe { self.get_non_send_mut_with_id(component_id) } + // SAFETY: caller ensures that there is no `&mut World` so we have permission to access world metadata + let component_id = unsafe { self.components() }.get_resource_id(TypeId::of::())?; + // SAFETY: + // - caller ensures that `self` has permission to access the resource + // - caller ensures that the resource is unaliased + unsafe { + self.get_non_send_resource_mut_by_id(component_id) + // SAFETY: `component_id` was gotten by `TypeId::of::()` + .map(|ptr| ptr.with_type::()) + } } /// Gets a `!Send` resource to the resource with the id [`ComponentId`] if it exists. @@ -280,11 +455,18 @@ impl<'w> UnsafeWorldCell<'w> { /// - no other references to the resource exist at the same time #[inline] pub unsafe fn get_non_send_resource_mut_by_id( - &mut self, + self, component_id: ComponentId, - ) -> Option> { - let change_tick = self.read_change_tick(); - let (ptr, ticks) = self.0.get_non_send_with_ticks(component_id)?; + ) -> Option> { + // SAFETY: caller ensures there is no `&mut World` + let change_tick = unsafe { self.read_change_tick() }; + // SAFETY: we only access data that the caller has ensured is unaliased and `self` + // has permission to access. + let (ptr, ticks) = unsafe { self.unsafe_world() } + .storages + .non_send_resources + .get(component_id)? + .get_with_ticks()?; let ticks = // SAFETY: This function has exclusive access to the world so nothing aliases `ticks`. @@ -298,59 +480,52 @@ impl<'w> UnsafeWorldCell<'w> { ticks, }) } -} -impl<'w> UnsafeWorldCell<'w> { + // Shorthand helper function for getting the data and change ticks for a resource. + /// /// # Safety /// It is the callers responsibility to ensure that - /// - `component_id` must be assigned to a component of type `R` - /// - the [`UnsafeWorldCell`] has permission to access the resource - /// - no other mutable references to the resource exist at the same time + /// - the [`UnsafeWorldCell`] has permission to access the resource mutably + /// - no mutable references to the resource exist at the same time #[inline] - pub(crate) unsafe fn get_resource_mut_with_id( - &self, + pub(crate) unsafe fn get_resource_with_ticks( + self, component_id: ComponentId, - ) -> Option> { - let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?; - + ) -> Option<(Ptr<'w>, TickCells<'w>)> { // SAFETY: - // - This caller ensures that nothing aliases `ticks`. - // - index is in-bounds because the column is initialized and non-empty - let ticks = unsafe { - TicksMut::from_tick_cells(ticks, self.last_change_tick(), self.read_change_tick()) - }; - - Some(Mut { - // SAFETY: caller ensures aliasing rules, ptr is of type `R` - value: unsafe { ptr.assert_unique().deref_mut() }, - ticks, - }) + // - caller ensures there is no `&mut World` + // - caller ensures there are no mutable borrows of this resource + // - caller ensures that we have permission to access this resource + unsafe { self.unsafe_world() } + .storages + .resources + .get(component_id)? + .get_with_ticks() } + // Shorthand helper function for getting the data and change ticks for a resource. + /// + /// # Panics + /// This function will panic if it isn't called from the same thread that the resource was inserted from. + /// /// # Safety /// It is the callers responsibility to ensure that - /// - `component_id` must be assigned to a component of type `R`. /// - the [`UnsafeWorldCell`] has permission to access the resource mutably - /// - no other references to the resource exist at the same time + /// - no mutable references to the resource exist at the same time #[inline] - pub(crate) unsafe fn get_non_send_mut_with_id( - &self, + pub(crate) unsafe fn get_non_send_with_ticks( + self, component_id: ComponentId, - ) -> Option> { - let (ptr, ticks) = self - .0 + ) -> Option<(Ptr<'w>, TickCells<'w>)> { + // SAFETY: + // - caller ensures there is no `&mut World` + // - caller ensures there are no mutable borrows of this resource + // - caller ensures that we have permission to access this resource + unsafe { self.unsafe_world() } .storages .non_send_resources .get(component_id)? - .get_with_ticks()?; - Some(Mut { - // SAFETY: caller ensures unique access - value: unsafe { ptr.assert_unique().deref_mut() }, - // SAFETY: caller ensures unique access - ticks: unsafe { - TicksMut::from_tick_cells(ticks, self.last_change_tick(), self.read_change_tick()) - }, - }) + .get_with_ticks() } } @@ -386,9 +561,13 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { self.location } + /// # Safety + /// - there must not be a `&mut World` #[inline] - pub fn archetype(self) -> &'w Archetype { - &self.world.0.archetypes[self.location.archetype_id] + pub unsafe fn archetype(self) -> &'w Archetype { + // SAFETY: + // - caller ensures there is no `&mut World` + unsafe { &self.world.archetypes()[self.location.archetype_id] } } #[inline] @@ -396,19 +575,37 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { self.world } + /// # Safety + /// - there must not be a `&mut World` #[inline] - pub fn contains(self) -> bool { - self.contains_type_id(TypeId::of::()) + pub unsafe fn contains(self) -> bool { + // SAFETY: + // - caller ensures there is no `&mut World` + unsafe { self.contains_type_id(TypeId::of::()) } } + /// # Safety + /// - there must not be a `&mut World` #[inline] - pub fn contains_id(self, component_id: ComponentId) -> bool { - entity_ref::contains_component_with_id(self.world.0, component_id, self.location) + pub unsafe fn contains_id(self, component_id: ComponentId) -> bool { + // SAFETY: + // - caller ensures there is no `&mut World` + unsafe { self.archetype() }.contains(component_id) } + /// # Safety + /// - there must not be a `&mut World` #[inline] - pub fn contains_type_id(self, type_id: TypeId) -> bool { - entity_ref::contains_component_with_type(self.world.0, type_id, self.location) + pub unsafe fn contains_type_id(self, type_id: TypeId) -> bool { + // SAFETY: + // - caller ensures there is no `&mut World` + let id = match unsafe { self.world.components() }.get_id(type_id) { + Some(id) => id, + None => return false, + }; + // SAFETY: + // - caller ensures there is no `&mut World` + unsafe { self.contains_id(id) } } /// # Safety @@ -417,20 +614,23 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { /// - no other mutable references to the component exist at the same time #[inline] pub unsafe fn get(self) -> Option<&'w T> { + // SAFETY: + // - caller ensures there is no `&mut World` + let component_id = unsafe { self.world.components() }.get_id(TypeId::of::())?; + // SAFETY: // - entity location is valid // - proper world access is promised by caller unsafe { - self.world - .0 - .get_component_with_type( - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - // SAFETY: returned component is of type T - .map(|value| value.deref::()) + get_component( + self.world, + component_id, + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + // SAFETY: returned component is of type T + .map(|value| value.deref::()) } } @@ -443,12 +643,17 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { /// - no other mutable references to the component exist at the same time #[inline] pub unsafe fn get_change_ticks(self) -> Option { + // SAFETY: + // - caller ensures there is no `&mut World` + let component_id = unsafe { self.world.components() }.get_id(TypeId::of::())?; + // SAFETY: // - entity location is valid // - proper world acess is promised by caller unsafe { - self.world.0.get_ticks_with_type( - TypeId::of::(), + get_ticks( + self.world, + component_id, T::Storage::STORAGE_TYPE, self.entity, self.location, @@ -472,13 +677,16 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { &self, component_id: ComponentId, ) -> Option { - let info = self.world.components().get_info(component_id)?; + // SAFETY: + // - caller ensures there is no `&mut World` + let info = unsafe { self.world.components() }.get_info(component_id)?; // SAFETY: // - entity location and entity is valid // - world access is immutable, lifetime tied to `&self` // - the storage type provided is correct for T unsafe { - self.world.0.get_ticks( + get_ticks( + self.world, component_id, info.storage_type(), self.entity, @@ -509,23 +717,26 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { last_change_tick: u32, change_tick: u32, ) -> Option> { + // SAFETY: + // - caller ensures there is no `&mut World` + let component_id = unsafe { self.world.components() }.get_id(TypeId::of::())?; + // SAFETY: // - `storage_type` is correct // - `location` is valid // - aliasing rules are ensured by caller unsafe { - self.world - .0 - .get_component_and_ticks_with_type( - TypeId::of::(), - T::Storage::STORAGE_TYPE, - self.entity, - self.location, - ) - .map(|(value, cells)| Mut { - value: value.assert_unique().deref_mut::(), - ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), - }) + get_component_and_ticks( + self.world, + component_id, + T::Storage::STORAGE_TYPE, + self.entity, + self.location, + ) + .map(|(value, cells)| Mut { + value: value.assert_unique().deref_mut::(), + ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), + }) } } } @@ -546,10 +757,13 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { /// - no other mutable references to the component exist at the same time #[inline] pub unsafe fn get_by_id(self, component_id: ComponentId) -> Option> { - let info = self.world.0.components.get_info(component_id)?; + // SAFETY: + // - caller ensures there is no `&mut World` + let info = unsafe { self.world.components() }.get_info(component_id)?; // SAFETY: entity_location is valid, component_id is valid as checked by the line above unsafe { - self.world.0.get_component( + get_component( + self.world, component_id, info.storage_type(), self.entity, @@ -570,26 +784,138 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { /// - no other references to the component exist at the same time #[inline] pub unsafe fn get_mut_by_id(self, component_id: ComponentId) -> Option> { - let info = self.world.0.components.get_info(component_id)?; + // SAFETY: + // - caller ensures there is no `&mut World` + let info = unsafe { self.world.components() }.get_info(component_id)?; // SAFETY: entity_location is valid, component_id is valid as checked by the line above unsafe { - self.world - .0 - .get_component_and_ticks( - component_id, - info.storage_type(), - self.entity, - self.location, - ) - .map(|(value, cells)| MutUntyped { - // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime - value: value.assert_unique(), - ticks: TicksMut::from_tick_cells( - cells, - self.world.last_change_tick(), - self.world.read_change_tick(), - ), - }) + get_component_and_ticks( + self.world, + component_id, + info.storage_type(), + self.entity, + self.location, + ) + .map(|(value, cells)| MutUntyped { + // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime + value: value.assert_unique(), + ticks: TicksMut::from_tick_cells( + cells, + self.world.last_change_tick(), + self.world.read_change_tick(), + ), + }) } } } + +/// Get an untyped 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] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn get_component( + world: UnsafeWorldCell<'_>, + 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(world, 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(world, component_id)?.get(entity), + } +} + +/// Get an untyped 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] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn get_component_and_ticks( + world: UnsafeWorldCell<'_>, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, +) -> Option<(Ptr<'_>, TickCells<'_>)> { + match storage_type { + StorageType::Table => { + let (components, table_row) = fetch_table(world, 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(world, component_id)?.get_with_ticks(entity), + } +} + +/// Get an untyped 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] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn get_ticks( + world: UnsafeWorldCell<'_>, + component_id: ComponentId, + storage_type: StorageType, + entity: Entity, + location: EntityLocation, +) -> Option { + match storage_type { + StorageType::Table => { + let (components, table_row) = fetch_table(world, 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(world, component_id)?.get_ticks(entity), + } +} + +#[inline] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn fetch_table( + world: UnsafeWorldCell<'_>, + location: EntityLocation, + component_id: ComponentId, +) -> Option<(&Column, TableRow)> { + let archetype = &world.archetypes()[location.archetype_id]; + let table = &world.unsafe_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] +#[allow(unsafe_op_in_unsafe_fn)] +unsafe fn fetch_sparse_set( + world: UnsafeWorldCell<'_>, + component_id: ComponentId, +) -> Option<&ComponentSparseSet> { + world.unsafe_world().storages.sparse_sets.get(component_id) +} diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index 9ea673ca02f34..2af1548e0ccfe 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -197,9 +197,14 @@ impl<'w> WorldCell<'w> { /// Gets a reference to the resource of the given type pub fn get_resource(&self) -> Option> { - let component_id = self.world.components().get_resource_id(TypeId::of::())?; + // SAFETY: `WorldCell` only makes a `&mut World` on drop so its fine to access metadata here + let component_id = unsafe { self.world.components() }.get_resource_id(TypeId::of::())?; - let archetype_component_id = self.world.storages().resources.get(component_id)?.id(); + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let archetype_component_id = unsafe { + self.world + .get_resource_archetype_component_id(component_id)? + }; WorldBorrow::try_new( // SAFETY: access is checked by WorldBorrow @@ -230,8 +235,14 @@ impl<'w> WorldCell<'w> { /// Gets a mutable reference to the resource of the given type pub fn get_resource_mut(&self) -> Option> { - let component_id = self.world.components().get_resource_id(TypeId::of::())?; - let archetype_component_id = self.world.storages().resources.get(component_id)?.id(); + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let component_id = unsafe { self.world.components() }.get_resource_id(TypeId::of::())?; + + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let archetype_component_id = unsafe { + self.world + .get_resource_archetype_component_id(component_id)? + }; WorldBorrowMut::try_new( // SAFETY: access is checked by WorldBorrowMut || unsafe { self.world.get_resource_mut::() }, @@ -261,13 +272,14 @@ impl<'w> WorldCell<'w> { /// Gets an immutable reference to the non-send resource of the given type, if it exists. pub fn get_non_send_resource(&self) -> Option> { - let component_id = self.world.components().get_resource_id(TypeId::of::())?; - let archetype_component_id = self - .world - .storages() - .non_send_resources - .get(component_id)? - .id(); + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let component_id = unsafe { self.world.components() }.get_resource_id(TypeId::of::())?; + + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let archetype_component_id = unsafe { + self.world + .get_non_send_archetype_component_id(component_id)? + }; WorldBorrow::try_new( // SAFETY: access is checked by WorldBorrowMut || unsafe { self.world.get_non_send_resource::() }, @@ -297,13 +309,14 @@ impl<'w> WorldCell<'w> { /// Gets a mutable reference to the non-send resource of the given type, if it exists. pub fn get_non_send_resource_mut(&self) -> Option> { - let component_id = self.world.components().get_resource_id(TypeId::of::())?; - let archetype_component_id = self - .world - .storages() - .non_send_resources - .get(component_id)? - .id(); + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let component_id = unsafe { self.world.components() }.get_resource_id(TypeId::of::())?; + + // SAFETY: `WorldCell` onyl makes a `&mut World` on drop so its fine to access metadata here + let archetype_component_id = unsafe { + self.world + .get_non_send_archetype_component_id(component_id)? + }; WorldBorrowMut::try_new( // SAFETY: access is checked by WorldBorrowMut || unsafe { self.world.get_non_send_resource_mut::() },