diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 76ad5d447c..75d0c215e8 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -446,6 +446,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!( @@ -486,6 +487,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(), @@ -540,7 +542,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: {}", @@ -549,7 +551,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, @@ -578,9 +580,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, @@ -889,6 +891,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!( @@ -927,6 +930,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(), @@ -979,6 +983,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!( @@ -1011,6 +1016,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 cfefb07076..679416ff7e 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}, removal_detection::RemovedComponentEvents, storage::Storages, @@ -79,12 +77,14 @@ impl<'w> EntityRef<'w> { #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + 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) + self.as_unsafe_world_cell_readonly() + .contains_type_id(type_id) } #[inline] @@ -209,12 +209,14 @@ impl<'w> EntityMut<'w> { #[inline] pub fn contains_id(&self, component_id: ComponentId) -> bool { - contains_component_with_id(self.world, component_id, self.location) + 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) + self.as_unsafe_world_cell_readonly() + .contains_type_id(type_id) } #[inline] @@ -600,19 +602,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. @@ -625,32 +618,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) } + // 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_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 - } -} - -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. @@ -768,59 +742,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 6029bd43ea..face0b33ef 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -12,23 +12,19 @@ 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}, removal_detection::RemovedComponentEvents, schedule::{Schedule, ScheduleLabel, Schedules}, - storage::{Column, ComponentSparseSet, ResourceData, Storages, TableRow}, + storage::{ResourceData, Storages}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr}; use bevy_utils::tracing::warn; use std::{ any::TypeId, - cell::UnsafeCell, fmt, sync::atomic::{AtomicU32, Ordering}, }; @@ -66,7 +62,7 @@ pub struct World { pub(crate) bundles: Bundles, pub(crate) removed_components: RemovedComponentEvents, /// Access cache used by [WorldCell]. Is only accessed in the `Drop` impl of `WorldCell`. - pub(crate) archetype_component_access: UnsafeCell, + pub(crate) archetype_component_access: ArchetypeComponentAccess, pub(crate) change_tick: AtomicU32, pub(crate) last_change_tick: u32, pub(crate) last_check_tick: u32, @@ -112,19 +108,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 @@ -602,9 +598,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 @@ -1005,15 +1002,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() } } @@ -1098,9 +1098,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. @@ -1110,34 +1111,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( @@ -1380,36 +1359,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 @@ -1616,7 +1565,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. @@ -1627,7 +1582,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) @@ -1645,10 +1602,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. @@ -1662,20 +1622,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]. @@ -1715,18 +1668,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) } } @@ -1741,183 +1689,17 @@ 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, - ) + self.as_unsafe_world_cell() + .get_entity(entity)? + .get_mut_by_id(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), - } - } - - #[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) - } -} - // Schedule-related methods impl World { /// Runs the [`Schedule`] associated with the `label` a single time. diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 4f3539e75d..ef36097646 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,177 @@ 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) + /// Creates a [`UnsafeWorldCell`] that can be used to access everything immutably + pub(crate) fn new_readonly(world: &'w World) -> Self { + UnsafeWorldCell(world as *const World as *mut World, PhantomData) + } + + /// Creates [`UnsafeWorldCell`] that can be used to access everything mutably + pub(crate) fn new_mutable(world: &'w mut World) -> Self { + Self(world 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. + /// + /// # Safety + /// - must have permission to access everything mutably + /// - there must be no other borrows on world + /// - returned borrow must not be used in any way if the world is accessed + /// through other means than the `&mut World` after this call. + 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 to call methods like [`World::contains_resource`] which aren't exposed and but don't perform any accesses. - /// - /// **Note**: You *must not* hand out a `&World` reference to arbitrary safe code when the [`UnsafeWorldCell`] is currently - /// being used for mutable accesses. + /// 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. + /// - must have permission to access the whole world immutably + /// - 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 } + } + + /// Gets a reference to the [`World`] this [`UnsafeWorldCell`] belong to. + /// This can be used for arbitrary read only access of world metadata + /// + /// You should attempt to use various safe methods on [`UnsafeWorldCell`] for + /// metadata access before using this method. + /// + /// # Safety + /// - must only be used to access world metadata + pub unsafe fn world_metadata(self) -> &'w World { + // SAFETY: caller ensures that returned reference is not used to violate aliasing rules + unsafe { self.unsafe_world() } + } + + /// 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 + /// - must not be used in a way that would conflict with any + /// live exclusive borrows on world data + unsafe fn unsafe_world(self) -> &'w World { + // SAFETY: + // - 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 #[inline] pub fn entities(self) -> &'w Entities { - &self.0.entities + // SAFETY: + // - we only access world metadata + &unsafe { self.world_metadata() }.entities } /// Retrieves this world's [Archetypes] collection #[inline] pub fn archetypes(self) -> &'w Archetypes { - &self.0.archetypes + // SAFETY: + // - we only access world metadata + &unsafe { self.world_metadata() }.archetypes } /// Retrieves this world's [Components] collection #[inline] pub fn components(self) -> &'w Components { - &self.0.components - } - - /// Retrieves this world's [Storages] collection - #[inline] - pub fn storages(self) -> &'w Storages { - &self.0.storages + // SAFETY: + // - we only access world metadata + &unsafe { self.world_metadata() }.components } /// Retrieves this world's [Bundles] collection #[inline] pub fn bundles(self) -> &'w Bundles { - &self.0.bundles + // SAFETY: + // - we only access world metadata + &unsafe { self.world_metadata() }.bundles } /// Reads the current change tick of this world. #[inline] pub fn read_change_tick(self) -> u32 { - self.0.read_change_tick() + // SAFETY: + // - we only access world metadata + unsafe { self.world_metadata() } + .change_tick + .load(Ordering::Acquire) } #[inline] pub fn last_change_tick(self) -> u32 { - self.0.last_change_tick() + // SAFETY: + // - we only access world metadata + unsafe { self.world_metadata() }.last_change_tick } #[inline] pub fn increment_change_tick(self) -> u32 { - self.0.increment_change_tick() + // SAFETY: + // - we only access world metadata + unsafe { self.world_metadata() } + .change_tick + .fetch_add(1, Ordering::AcqRel) + } + + /// Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. + #[inline] + pub(crate) fn get_resource_archetype_component_id( + self, + component_id: ComponentId, + ) -> Option { + // SAFETY: + // - we only access world metadata + let resource = unsafe { self.world_metadata() } + .storages + .resources + .get(component_id)?; + Some(resource.id()) + } + + /// Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. + #[inline] + pub(crate) fn get_non_send_archetype_component_id( + self, + component_id: ComponentId, + ) -> Option { + // SAFETY: + // - we only access world metadata + let resource = unsafe { self.world_metadata() } + .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)?; + let location = self.entities().get(entity)?; Some(UnsafeWorldCellEntityRef::new(self, entity, location)) } @@ -150,7 +254,14 @@ impl<'w> UnsafeWorldCell<'w> { /// - no mutable reference to the resource exists at the same time #[inline] pub unsafe fn get_resource(self) -> Option<&'w R> { - self.0.get_resource::() + let component_id = 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 +277,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 @@ -177,7 +294,14 @@ impl<'w> UnsafeWorldCell<'w> { /// - no mutable reference to the resource exists at the same time #[inline] pub unsafe fn get_non_send_resource(self) -> Option<&'w R> { - self.0.get_non_send_resource() + let component_id = 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 +320,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)? @@ -211,12 +337,15 @@ impl<'w> UnsafeWorldCell<'w> { /// - no other references to the resource exist at the same time #[inline] pub unsafe fn get_resource_mut(self) -> Option> { - let component_id = self.0.components.get_resource_id(TypeId::of::())?; + let component_id = 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 +361,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 @@ -259,9 +394,15 @@ impl<'w> UnsafeWorldCell<'w> { /// - no other references to the resource exist at the same time #[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) } + let component_id = 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 +421,17 @@ 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> { + ) -> Option> { let change_tick = self.read_change_tick(); - let (ptr, ticks) = self.0.get_non_send_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 + .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 +445,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() } } @@ -388,7 +528,7 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { #[inline] pub fn archetype(self) -> &'w Archetype { - &self.world.0.archetypes[self.location.archetype_id] + &self.world.archetypes()[self.location.archetype_id] } #[inline] @@ -403,12 +543,16 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { #[inline] pub fn contains_id(self, component_id: ComponentId) -> bool { - entity_ref::contains_component_with_id(self.world.0, component_id, self.location) + self.archetype().contains(component_id) } #[inline] pub fn contains_type_id(self, type_id: TypeId) -> bool { - entity_ref::contains_component_with_type(self.world.0, type_id, self.location) + let id = match self.world.components().get_id(type_id) { + Some(id) => id, + None => return false, + }; + self.contains_id(id) } /// # Safety @@ -417,20 +561,21 @@ 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> { + let component_id = 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 +588,15 @@ 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 { + let component_id = 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, @@ -478,7 +626,8 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { // - 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 +658,24 @@ impl<'w> UnsafeWorldCellEntityRef<'w> { last_change_tick: u32, change_tick: u32, ) -> Option> { + let component_id = 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 +696,11 @@ 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)?; + let info = 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 +721,148 @@ 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)?; + let info = 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(), + ), + }) } } } + +impl<'w> UnsafeWorldCell<'w> { + #[inline] + /// # Safety: + /// - the returned `Column` is only used in ways that this [`UnsafeWorldCell`] has permission for. + /// - the returned `Column` is only used in ways that would not conflict with any existing + /// borrows of world data. + unsafe fn fetch_table( + self, + location: EntityLocation, + component_id: ComponentId, + ) -> Option<(&'w Column, TableRow)> { + let archetype = &self.archetypes()[location.archetype_id]; + // SAFETY: caller ensures returned data is not misused and we have not created any borrows + // of component/resource data + let table = &unsafe { self.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] + /// # Safety: + /// - the returned `ComponentSparseSet` is only used in ways that this [`UnsafeWorldCell`] has permission for. + /// - the returned `ComponentSparseSet` is only used in ways that would not conflict with any existing + /// borrows of world data. + unsafe fn fetch_sparse_set(self, component_id: ComponentId) -> Option<&'w ComponentSparseSet> { + // SAFETY: caller ensures returned data is not misused and we have not created any borrows + // of component/resource data + unsafe { self.unsafe_world() } + .storages + .sparse_sets + .get(component_id) + } +} + +/// 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) = world.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 => world.fetch_sparse_set(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) = world.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 => world.fetch_sparse_set(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) = world.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 => world.fetch_sparse_set(component_id)?.get_ticks(entity), + } +} diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index 9ea673ca02..73dabe387c 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -80,10 +80,10 @@ impl<'w> Drop for WorldCell<'w> { let mut access = self.access.borrow_mut(); { - // SAFETY: we only swap `archetype_component_access` - let world = unsafe { self.world.world() }; - // SAFETY: the WorldCell has exclusive world access - let world_cached_access = unsafe { &mut *world.archetype_component_access.get() }; + // SAFETY: `WorldCell` does not hand out `UnsafeWorldCell` to anywhere else so this is the only + // `UnsafeWorldCell` and we have exclusive access to it. + let world = unsafe { self.world.world_mut() }; + let world_cached_access = &mut world.archetype_component_access; // give world ArchetypeComponentAccess back to reuse allocations std::mem::swap(world_cached_access, &mut *access); @@ -185,7 +185,7 @@ impl<'w> WorldCell<'w> { pub(crate) fn new(world: &'w mut World) -> Self { // this is cheap because ArchetypeComponentAccess::new() is const / allocation free let access = std::mem::replace( - world.archetype_component_access.get_mut(), + &mut world.archetype_component_access, ArchetypeComponentAccess::new(), ); // world's ArchetypeComponentAccess is recycled to cut down on allocations @@ -199,7 +199,9 @@ impl<'w> WorldCell<'w> { pub fn get_resource(&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(); + let archetype_component_id = self + .world + .get_resource_archetype_component_id(component_id)?; WorldBorrow::try_new( // SAFETY: access is checked by WorldBorrow @@ -231,7 +233,10 @@ 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(); + + let archetype_component_id = self + .world + .get_resource_archetype_component_id(component_id)?; WorldBorrowMut::try_new( // SAFETY: access is checked by WorldBorrowMut || unsafe { self.world.get_resource_mut::() }, @@ -262,12 +267,10 @@ 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(); + .get_non_send_archetype_component_id(component_id)?; WorldBorrow::try_new( // SAFETY: access is checked by WorldBorrowMut || unsafe { self.world.get_non_send_resource::() }, @@ -298,12 +301,10 @@ 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(); + .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::() }, @@ -422,11 +423,10 @@ mod tests { let u32_archetype_component_id = world .get_resource_archetype_component_id(u32_component_id) .unwrap(); - assert_eq!(world.archetype_component_access.get_mut().access.len(), 1); + assert_eq!(world.archetype_component_access.access.len(), 1); assert_eq!( world .archetype_component_access - .get_mut() .access .get(u32_archetype_component_id), Some(&BASE_ACCESS),