Move all logic to UnsafeWorldCell (#7381)

# Objective

- Implementing logic used by system params and `UnsafeWorldCell` on `&World` is sus since `&World` generally denotes shared read only access to world but this is a lie in the above situations. Move most/all logic that uses `&World` to mean `UnsafeWorldCell` onto `UnsafeWorldCell`
-  Add a way to take a `&mut World` out of `UnsafeWorldCell` and use this in `WorldCell`'s `Drop` impl instead of a `UnsafeCell` field

---

## Changelog

- changed some `UnsafeWorldCell` methods to take `self` instead of `&self`/`&mut self` since there is literally no point to them doing that
- `UnsafeWorldCell::world` is now used to get immutable access to the whole world instead of just the metadata which can now be done via `UnsafeWorldCell::world_metadata`
- `UnsafeWorldCell::world_mut` now exists and can be used to get a `&mut World` out of `UnsafeWorldCell`
- removed `UnsafeWorldCell::storages` since that is probably unsound since storages contains the actual component/resource data not just metadata

## Migration guide

N/A none of the breaking changes here make any difference for a 0.9->0.10 transition since `UnsafeWorldCell` did not exist in 0.9
This commit is contained in:
Boxy 2023-02-06 19:02:52 +00:00
parent aa4170d9a4
commit 5c680a38a7
5 changed files with 506 additions and 524 deletions

View file

@ -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<Res<'a, T>> {
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::<T>(),
ticks: TicksMut {
added: value.ticks.added,
changed: value.ticks.changed,
@ -578,9 +580,9 @@ unsafe impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
) -> 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::<T>(),
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<T: 'static> SystemParam for Option<NonSend<'_, T>> {
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<NonSendMut<'a, T>> {
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(),

View file

@ -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<Ptr<'_>> {
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<MutUntyped<'_>> {
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<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
});
}
// SAFETY: EntityLocation must be valid
#[inline]
pub(crate) unsafe fn get_mut<T: Component>(
world: &mut World,
entity: Entity,
location: EntityLocation,
) -> Option<Mut<'_, T>> {
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>(),
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::<T>(),
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<MutUntyped<'_>> {
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

View file

@ -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<ArchetypeComponentAccess>,
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<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
// 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<R: Resource>(&self) -> Option<&R> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
// 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<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
// 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<R: 'static>(&self) -> Option<&R> {
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
// 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<R: 'static>(&mut self) -> Option<Mut<'_, R>> {
// 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<R: 'static>(
&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<R: 'static>(
&self,
component_id: ComponentId,
) -> Option<&R> {
Some(
self.storages
.non_send_resources
.get(component_id)?
.get_data()?
.deref::<R>(),
)
}
/// 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<Ptr<'_>> {
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<MutUntyped<'_>> {
// 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<Ptr<'_>> {
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<MutUntyped<'_>> {
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<Ptr<'_>> {
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<MutUntyped<'_>> {
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<Ptr<'_>> {
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<Ptr<'_>> {
// 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<ComponentTicks> {
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<ComponentTicks> {
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.

View file

@ -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<World>)>);
// 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<ArchetypeComponentId> {
// 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<ArchetypeComponentId> {
// 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<UnsafeWorldCellEntityRef<'w>> {
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<R: Resource>(self) -> Option<&'w R> {
self.0.get_resource::<R>()
let component_id = self.components().get_resource_id(TypeId::of::<R>())?;
// 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::<R>())
}
}
/// 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<Ptr<'w>> {
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<R: 'static>(self) -> Option<&'w R> {
self.0.get_non_send_resource()
let component_id = self.components().get_resource_id(TypeId::of::<R>())?;
// 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::<R>()`
.map(|ptr| ptr.deref::<R>())
}
}
/// 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<Ptr<'w>> {
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<R: Resource>(self) -> Option<Mut<'w, R>> {
let component_id = self.0.components.get_resource_id(TypeId::of::<R>())?;
let component_id = self.components().get_resource_id(TypeId::of::<R>())?;
// 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::<R>()`
.map(|ptr| ptr.with_type::<R>())
}
}
/// 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<MutUntyped<'w>> {
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<R: 'static>(self) -> Option<Mut<'w, R>> {
let component_id = self.0.components.get_resource_id(TypeId::of::<R>())?;
// 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::<R>())?;
// 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::<R>()`
.map(|ptr| ptr.with_type::<R>())
}
}
/// 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<MutUntyped<'_>> {
) -> Option<MutUntyped<'w>> {
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
#[inline]
pub(crate) unsafe fn get_resource_mut_with_id<R>(
&self,
component_id: ComponentId,
) -> Option<Mut<'w, R>> {
let (ptr, ticks) = self.0.get_resource_with_ticks(component_id)?;
// 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,
})
}
/// # 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<R: 'static>(
&self,
pub(crate) unsafe fn get_resource_with_ticks(
self,
component_id: ComponentId,
) -> Option<Mut<'w, R>> {
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
.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
/// - 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_non_send_with_ticks(
self,
component_id: ComponentId,
) -> 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,14 +561,15 @@ impl<'w> UnsafeWorldCellEntityRef<'w> {
/// - no other mutable references to the component exist at the same time
#[inline]
pub unsafe fn get<T: Component>(self) -> Option<&'w T> {
let component_id = self.world.components().get_id(TypeId::of::<T>())?;
// SAFETY:
// - entity location is valid
// - proper world access is promised by caller
unsafe {
self.world
.0
.get_component_with_type(
TypeId::of::<T>(),
get_component(
self.world,
component_id,
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
@ -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<T: Component>(self) -> Option<ComponentTicks> {
let component_id = self.world.components().get_id(TypeId::of::<T>())?;
// SAFETY:
// - entity location is valid
// - proper world acess is promised by caller
unsafe {
self.world.0.get_ticks_with_type(
TypeId::of::<T>(),
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,15 +658,16 @@ impl<'w> UnsafeWorldCellEntityRef<'w> {
last_change_tick: u32,
change_tick: u32,
) -> Option<Mut<'w, T>> {
let component_id = self.world.components().get_id(TypeId::of::<T>())?;
// 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>(),
get_component_and_ticks(
self.world,
component_id,
T::Storage::STORAGE_TYPE,
self.entity,
self.location,
@ -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<Ptr<'w>> {
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,12 +721,11 @@ 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<MutUntyped<'w>> {
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(
get_component_and_ticks(
self.world,
component_id,
info.storage_type(),
self.entity,
@ -593,3 +743,126 @@ impl<'w> UnsafeWorldCellEntityRef<'w> {
}
}
}
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<Ptr<'_>> {
// 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<ComponentTicks> {
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),
}
}

View file

@ -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<T: Resource>(&self) -> Option<WorldBorrow<'_, T>> {
let component_id = self.world.components().get_resource_id(TypeId::of::<T>())?;
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<T: Resource>(&self) -> Option<WorldBorrowMut<'_, T>> {
let component_id = self.world.components().get_resource_id(TypeId::of::<T>())?;
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::<T>() },
@ -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<T: 'static>(&self) -> Option<WorldBorrow<'_, T>> {
let component_id = self.world.components().get_resource_id(TypeId::of::<T>())?;
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::<T>() },
@ -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<T: 'static>(&self) -> Option<WorldBorrowMut<'_, T>> {
let component_id = self.world.components().get_resource_id(TypeId::of::<T>())?;
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::<T>() },
@ -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),