Skip to content

Commit

Permalink
Provide access to world storages via UnsafeWorldCell (#8987)
Browse files Browse the repository at this point in the history
# Objective

Title. This is necessary in order to update
[`bevy-trait-query`](https://crates.io/crates/bevy-trait-query) to Bevy
0.11.

---

## Changelog

Added the unsafe function `UnsafeWorldCell::storages`, which provides
unchecked access to the internal data stores of a `World`.
  • Loading branch information
JoJoJet authored Jun 29, 2023
1 parent e29981d commit de1dcb9
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 29 deletions.
3 changes: 0 additions & 3 deletions crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,6 @@ unsafe impl<T: Component> WorldQuery for &T {
// which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`.
.unsafe_world()
.storages()
.sparse_sets
.get(component_id)
Expand Down Expand Up @@ -810,7 +809,6 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
world
// SAFETY: See &T::init_fetch.
.unsafe_world()
.storages()
.sparse_sets
.get(component_id)
Expand Down Expand Up @@ -974,7 +972,6 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
world
// SAFETY: See &T::init_fetch.
.unsafe_world()
.storages()
.sparse_sets
.get(component_id)
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/query/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIter<'w, 's, Q, F> {
QueryIter {
query_state,
// SAFETY: We only access table data that has been registered in `query_state`.
tables: &world.unsafe_world().storages().tables,
tables: &world.storages().tables,
archetypes: world.archetypes(),
cursor: QueryIterationCursor::init(world, query_state, last_run, this_run),
}
Expand Down Expand Up @@ -107,7 +107,7 @@ where
archetypes: world.archetypes(),
// SAFETY: We only access table data that has been registered in `query_state`.
// This means `world` has permission to access the data we use.
tables: &world.unsafe_world().storages.tables,
tables: &world.storages().tables,
fetch,
filter,
entity_iter: entity_list.into_iter(),
Expand Down Expand Up @@ -316,7 +316,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize>
QueryCombinationIter {
query_state,
// SAFETY: We only access table data that has been registered in `query_state`.
tables: &world.unsafe_world().storages().tables,
tables: &world.storages().tables,
archetypes: world.archetypes(),
cursors: array.assume_init(),
}
Expand Down
9 changes: 4 additions & 5 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,6 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);

let table = world
.unsafe_world()
.storages()
.tables
.get(location.table_id)
Expand Down Expand Up @@ -973,7 +972,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);

let tables = &world.unsafe_world().storages().tables;
let tables = &world.storages().tables;
if Q::IS_DENSE && F::IS_DENSE {
for table_id in &self.matched_table_ids {
let table = tables.get(*table_id).debug_checked_unwrap();
Expand Down Expand Up @@ -1048,7 +1047,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
ComputeTaskPool::get().scope(|scope| {
if Q::IS_DENSE && F::IS_DENSE {
// SAFETY: We only access table data that has been registered in `self.archetype_component_access`.
let tables = &world.unsafe_world().storages().tables;
let tables = &world.storages().tables;
for table_id in &self.matched_table_ids {
let table = &tables[*table_id];
if table.is_empty() {
Expand All @@ -1064,7 +1063,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter =
F::init_fetch(world, &self.filter_state, last_run, this_run);
let tables = &world.unsafe_world().storages().tables;
let tables = &world.storages().tables;
let table = tables.get(*table_id).debug_checked_unwrap();
let entities = table.entities();
Q::set_table(&mut fetch, &self.fetch_state, table);
Expand Down Expand Up @@ -1108,7 +1107,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter =
F::init_fetch(world, &self.filter_state, last_run, this_run);
let tables = &world.unsafe_world().storages().tables;
let tables = &world.storages().tables;
let archetype =
world.archetypes().get(*archetype_id).debug_checked_unwrap();
let table = tables.get(archetype.table_id()).debug_checked_unwrap();
Expand Down
41 changes: 23 additions & 18 deletions crates/bevy_ecs/src/world/unsafe_world_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
},
entity::{Entities, Entity, EntityLocation},
prelude::Component,
storage::{Column, ComponentSparseSet},
storage::{Column, ComponentSparseSet, Storages},
system::Resource,
};
use bevy_ptr::Ptr;
Expand Down Expand Up @@ -271,6 +271,20 @@ impl<'w> UnsafeWorldCell<'w> {
unsafe { self.world_metadata() }.increment_change_tick()
}

/// Provides unchecked access to the internal data stores of the [`World`].
///
/// # Safety
///
/// The caller must ensure that this is only used to access world data
/// that this [`UnsafeWorldCell`] is allowed to.
/// As always, any mutable access to a component must not exist at the same
/// time as any other accesses to that same component.
#[inline]
pub unsafe fn storages(self) -> &'w Storages {
// SAFETY: The caller promises to only access world data allowed by this instance.
&unsafe { self.unsafe_world() }.storages
}

/// Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource.
#[inline]
pub(crate) fn get_resource_archetype_component_id(
Expand Down Expand Up @@ -342,8 +356,7 @@ impl<'w> UnsafeWorldCell<'w> {
pub unsafe fn get_resource_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
// SAFETY: caller ensures that `self` has permission to access `R`
// caller ensures that no mutable reference exists to `R`
unsafe { self.unsafe_world() }
.storages
unsafe { self.storages() }
.resources
.get(component_id)?
.get_data()
Expand Down Expand Up @@ -385,8 +398,7 @@ impl<'w> UnsafeWorldCell<'w> {
pub unsafe fn get_non_send_resource_by_id(self, component_id: ComponentId) -> Option<Ptr<'w>> {
// 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
unsafe { self.storages() }
.non_send_resources
.get(component_id)?
.get_data()
Expand Down Expand Up @@ -429,8 +441,7 @@ impl<'w> UnsafeWorldCell<'w> {
) -> Option<MutUntyped<'w>> {
// 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
let (ptr, ticks) = unsafe { self.storages() }
.resources
.get(component_id)?
.get_with_ticks()?;
Expand Down Expand Up @@ -492,8 +503,7 @@ impl<'w> UnsafeWorldCell<'w> {
let change_tick = self.change_tick();
// SAFETY: we only access data that the caller has ensured is unaliased and `self`
// has permission to access.
let (ptr, ticks) = unsafe { self.unsafe_world() }
.storages
let (ptr, ticks) = unsafe { self.storages() }
.non_send_resources
.get(component_id)?
.get_with_ticks()?;
Expand Down Expand Up @@ -526,8 +536,7 @@ impl<'w> UnsafeWorldCell<'w> {
// - 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
unsafe { self.storages() }
.resources
.get(component_id)?
.get_with_ticks()
Expand All @@ -551,8 +560,7 @@ impl<'w> UnsafeWorldCell<'w> {
// - 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
unsafe { self.storages() }
.non_send_resources
.get(component_id)?
.get_with_ticks()
Expand Down Expand Up @@ -878,7 +886,7 @@ impl<'w> UnsafeWorldCell<'w> {
) -> Option<&'w Column> {
// SAFETY: caller ensures returned data is not misused and we have not created any borrows
// of component/resource data
unsafe { self.unsafe_world() }.storages.tables[location.table_id].get_column(component_id)
unsafe { self.storages() }.tables[location.table_id].get_column(component_id)
}

#[inline]
Expand All @@ -889,10 +897,7 @@ impl<'w> UnsafeWorldCell<'w> {
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)
unsafe { self.storages() }.sparse_sets.get(component_id)
}
}

Expand Down

0 comments on commit de1dcb9

Please sign in to comment.