diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs
index cf81b5b633a2ee..9b9377e7ce2b9b 100644
--- a/crates/bevy_ecs/src/archetype.rs
+++ b/crates/bevy_ecs/src/archetype.rs
@@ -5,7 +5,7 @@ use crate::{
bundle::BundleId,
component::{ComponentId, StorageType},
entity::{Entity, EntityLocation},
- storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId},
+ storage::{SparseArray, SparseSet, SparseSetIndex, TableId},
};
use std::{
collections::HashMap,
@@ -18,7 +18,6 @@ pub struct ArchetypeId(usize);
impl ArchetypeId {
pub const EMPTY: ArchetypeId = ArchetypeId(0);
- pub const RESOURCE: ArchetypeId = ArchetypeId(1);
pub const INVALID: ArchetypeId = ArchetypeId(usize::MAX);
#[inline]
@@ -140,8 +139,7 @@ pub struct Archetype {
table_info: TableInfo,
table_components: Box<[ComponentId]>,
sparse_set_components: Box<[ComponentId]>,
- pub(crate) unique_components: SparseSet,
- pub(crate) components: SparseSet,
+ components: SparseSet,
}
impl Archetype {
@@ -188,7 +186,6 @@ impl Archetype {
components,
table_components,
sparse_set_components,
- unique_components: SparseSet::new(),
entities: Default::default(),
edges: Default::default(),
}
@@ -224,16 +221,6 @@ impl Archetype {
&self.sparse_set_components
}
- #[inline]
- pub fn unique_components(&self) -> &SparseSet {
- &self.unique_components
- }
-
- #[inline]
- pub fn unique_components_mut(&mut self) -> &mut SparseSet {
- &mut self.unique_components
- }
-
#[inline]
pub fn components(&self) -> impl Iterator- + '_ {
self.components.indices()
@@ -392,17 +379,6 @@ impl Default for Archetypes {
archetype_component_count: 0,
};
archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new());
-
- // adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
- // which prevents entities from being added to it
- archetypes.archetypes.push(Archetype::new(
- ArchetypeId::RESOURCE,
- TableId::empty(),
- Box::new([]),
- Box::new([]),
- Vec::new(),
- Vec::new(),
- ));
archetypes
}
}
@@ -433,21 +409,6 @@ impl Archetypes {
}
}
- #[inline]
- pub fn resource(&self) -> &Archetype {
- // SAFETY: resource archetype always exists
- unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) }
- }
-
- #[inline]
- pub(crate) fn resource_mut(&mut self) -> &mut Archetype {
- // SAFETY: resource archetype always exists
- unsafe {
- self.archetypes
- .get_unchecked_mut(ArchetypeId::RESOURCE.index())
- }
- }
-
#[inline]
pub fn is_empty(&self) -> bool {
self.archetypes.is_empty()
diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs
index 90fd426e43d72a..8dd9a7bf5e1c7c 100644
--- a/crates/bevy_ecs/src/lib.rs
+++ b/crates/bevy_ecs/src/lib.rs
@@ -956,11 +956,7 @@ mod tests {
.components()
.get_resource_id(TypeId::of::())
.unwrap();
- let archetype_component_id = world
- .archetypes()
- .resource()
- .get_archetype_component_id(resource_id)
- .unwrap();
+ let archetype_component_id = world.storages().resources.get(resource_id).unwrap().id();
assert_eq!(world.resource::().0, 123);
assert!(world.contains_resource::());
@@ -1023,11 +1019,8 @@ mod tests {
"resource id does not change after removing / re-adding"
);
- let current_archetype_component_id = world
- .archetypes()
- .resource()
- .get_archetype_component_id(current_resource_id)
- .unwrap();
+ let current_archetype_component_id =
+ world.storages().resources.get(resource_id).unwrap().id();
assert_eq!(
archetype_component_id, current_archetype_component_id,
diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs
index 571f05184cdbd0..6e848a042b492c 100644
--- a/crates/bevy_ecs/src/storage/mod.rs
+++ b/crates/bevy_ecs/src/storage/mod.rs
@@ -1,9 +1,11 @@
//! Storage layouts for ECS data.
mod blob_vec;
+mod resource;
mod sparse_set;
mod table;
+pub use resource::*;
pub use sparse_set::*;
pub use table::*;
@@ -12,4 +14,5 @@ pub use table::*;
pub struct Storages {
pub sparse_sets: SparseSets,
pub tables: Tables,
+ pub resources: Resources,
}
diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs
new file mode 100644
index 00000000000000..edfa51e12857a5
--- /dev/null
+++ b/crates/bevy_ecs/src/storage/resource.rs
@@ -0,0 +1,185 @@
+use crate::archetype::ArchetypeComponentId;
+use crate::component::{ComponentId, ComponentTicks, Components};
+use crate::storage::{Column, SparseSet};
+use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
+use std::cell::UnsafeCell;
+
+/// The type-erased backing storage and metadata for a single resource within a [`World`].
+///
+/// [`World`]: crate::world::World
+pub struct ResourceData {
+ column: Column,
+ id: ArchetypeComponentId,
+}
+
+impl ResourceData {
+ /// Returns true if the resource is populated.
+ #[inline]
+ pub fn is_present(&self) -> bool {
+ !self.column.is_empty()
+ }
+
+ /// Gets the [`ArchetypeComponentId`] for the resource.
+ #[inline]
+ pub fn id(&self) -> ArchetypeComponentId {
+ self.id
+ }
+
+ /// Gets a read-only pointer to the underlying resource, if available.
+ #[inline]
+ pub fn get_data(&self) -> Option> {
+ self.column.get_data(0)
+ }
+
+ /// Gets a read-only reference to the change ticks of the underlying resource, if available.
+ #[inline]
+ pub fn get_ticks(&self) -> Option<&ComponentTicks> {
+ self.column
+ .get_ticks(0)
+ // SAFETY:
+ // - This borrow's lifetime is bounded by the lifetime on self.
+ // - A read-only borrow on self can only exist while a mutable borrow doesn't
+ // exist.
+ .map(|ticks| unsafe { ticks.deref() })
+ }
+
+ #[inline]
+ pub(crate) fn get_with_ticks(&self) -> Option<(Ptr<'_>, &UnsafeCell)> {
+ self.column.get(0)
+ }
+
+ /// Inserts a value into the resource. If a value is already present
+ /// it will be replaced.
+ ///
+ /// # Safety
+ /// `value` must be valid for the underlying type for the resource.
+ ///
+ /// The underlying type must be [`Send`] or be inserted from the main thread.
+ /// This can be validated with [`World::validate_non_send_access_untyped`].
+ ///
+ /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
+ #[inline]
+ pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: u32) {
+ if self.is_present() {
+ self.column.replace(0, value, change_tick);
+ } else {
+ self.column.push(value, ComponentTicks::new(change_tick));
+ }
+ }
+
+ /// Inserts a value into the resource with a pre-existing change tick. If a
+ /// value is already present it will be replaced.
+ ///
+ /// # Safety
+ /// `value` must be valid for the underlying type for the resource.
+ ///
+ /// The underlying type must be [`Send`] or be inserted from the main thread.
+ /// This can be validated with [`World::validate_non_send_access_untyped`].
+ ///
+ /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
+ #[inline]
+ pub(crate) unsafe fn insert_with_ticks(
+ &mut self,
+ value: OwningPtr<'_>,
+ change_ticks: ComponentTicks,
+ ) {
+ if self.is_present() {
+ self.column.replace_untracked(0, value);
+ *self.column.get_ticks_unchecked(0).deref_mut() = change_ticks;
+ } else {
+ self.column.push(value, change_ticks);
+ }
+ }
+
+ /// Removes a value from the resource, if present.
+ ///
+ /// # Safety
+ /// The underlying type must be [`Send`] or be removed from the main thread.
+ /// This can be validated with [`World::validate_non_send_access_untyped`].
+ ///
+ /// The removed value must be used or dropped.
+ ///
+ /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
+ #[inline]
+ #[must_use = "The returned pointer to the removed component should be used or dropped"]
+ pub(crate) unsafe fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> {
+ self.column.swap_remove_and_forget(0)
+ }
+
+ /// Removes a value from the resource, if present, and drops it.
+ ///
+ /// # Safety
+ /// The underlying type must be [`Send`] or be removed from the main thread.
+ /// This can be validated with [`World::validate_non_send_access_untyped`].
+ ///
+ /// [`World::validate_non_send_access_untyped`]: crate::world::World::validate_non_send_access_untyped
+ #[inline]
+ pub(crate) unsafe fn remove_and_drop(&mut self) {
+ self.column.clear();
+ }
+}
+
+/// The backing store for all [`Resource`]s stored in the [`World`].
+///
+/// [`Resource`]: crate::system::Resource
+/// [`World`]: crate::world::World
+#[derive(Default)]
+pub struct Resources {
+ resources: SparseSet,
+}
+
+impl Resources {
+ /// The total number of resources stored in the [`World`]
+ ///
+ /// [`World`]: crate::world::World
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.resources.len()
+ }
+
+ /// Returns true if there are no resources stored in the [`World`],
+ /// false otherwise.
+ ///
+ /// [`World`]: crate::world::World
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.resources.is_empty()
+ }
+
+ /// Gets read-only access to a resource, if it exists.
+ #[inline]
+ pub fn get(&self, component_id: ComponentId) -> Option<&ResourceData> {
+ self.resources.get(component_id)
+ }
+
+ /// Gets mutable access to a resource, if it exists.
+ #[inline]
+ pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData> {
+ self.resources.get_mut(component_id)
+ }
+
+ /// Fetches or initializes a new resource and returns back it's underlying column.
+ ///
+ /// # Panics
+ /// Will panic if `component_id` is not valid for the provided `components`
+ pub(crate) fn initialize_with(
+ &mut self,
+ component_id: ComponentId,
+ components: &Components,
+ f: impl FnOnce() -> ArchetypeComponentId,
+ ) -> &mut ResourceData {
+ self.resources.get_or_insert_with(component_id, || {
+ let component_info = components.get_info(component_id).unwrap();
+ ResourceData {
+ column: Column::with_capacity(component_info, 1),
+ id: f(),
+ }
+ })
+ }
+
+ pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
+ for info in self.resources.values_mut() {
+ info.column.check_change_ticks(change_tick);
+ }
+ }
+}
diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs
index b0c84d898236a0..6e1e67f0abdeeb 100644
--- a/crates/bevy_ecs/src/storage/table.rs
+++ b/crates/bevy_ecs/src/storage/table.rs
@@ -86,6 +86,18 @@ impl Column {
.set_changed(change_tick);
}
+ /// Writes component data to the column at given row.
+ /// Assumes the slot is initialized, calls drop.
+ /// Does not update the Component's ticks.
+ ///
+ /// # Safety
+ /// Assumes data has already been allocated for the given row.
+ #[inline]
+ pub(crate) unsafe fn replace_untracked(&mut self, row: usize, data: OwningPtr<'_>) {
+ debug_assert!(row < self.len());
+ self.data.replace_unchecked(row, data);
+ }
+
#[inline]
pub fn len(&self) -> usize {
self.data.len()
@@ -104,6 +116,22 @@ impl Column {
self.ticks.swap_remove(row);
}
+ #[inline]
+ #[must_use = "The returned pointer should be used to drop the removed component"]
+ pub(crate) fn swap_remove_and_forget(
+ &mut self,
+ row: usize,
+ ) -> Option<(OwningPtr<'_>, ComponentTicks)> {
+ (row < self.data.len()).then(|| {
+ // SAFETY: The row was length checked before this.
+ let data = unsafe { self.data.swap_remove_and_forget_unchecked(row) };
+ let ticks = self.ticks.swap_remove(row).into_inner();
+ (data, ticks)
+ })
+ }
+
+ /// # Safety
+ /// index must be in-bounds
#[inline]
#[must_use = "The returned pointer should be used to dropped the removed component"]
pub(crate) unsafe fn swap_remove_and_forget_unchecked(
@@ -168,6 +196,21 @@ impl Column {
&self.ticks
}
+ #[inline]
+ pub fn get(&self, row: usize) -> Option<(Ptr<'_>, &UnsafeCell)> {
+ (row < self.data.len())
+ // SAFETY: The row is length checked before fetching the pointer. This is being
+ // accessed through a read-only reference to the column.
+ .then(|| unsafe { (self.data.get_unchecked(row), self.ticks.get_unchecked(row)) })
+ }
+
+ #[inline]
+ pub fn get_data(&self, row: usize) -> Option> {
+ // SAFETY: The row is length checked before fetching the pointer. This is being
+ // accessed through a read-only reference to the column.
+ (row < self.data.len()).then(|| unsafe { self.data.get_unchecked(row) })
+ }
+
/// # Safety
/// - index must be in-bounds
/// - no other reference to the data of the same row can exist at the same time
@@ -177,6 +220,13 @@ impl Column {
self.data.get_unchecked(row)
}
+ #[inline]
+ pub fn get_data_mut(&mut self, row: usize) -> Option> {
+ // SAFETY: The row is length checked before fetching the pointer. This is being
+ // accessed through an exclusive reference to the column.
+ (row < self.data.len()).then(|| unsafe { self.data.get_unchecked_mut(row) })
+ }
+
/// # Safety
/// - index must be in-bounds
/// - no other reference to the data of the same row can exist at the same time
@@ -186,6 +236,11 @@ impl Column {
self.data.get_unchecked_mut(row)
}
+ #[inline]
+ pub fn get_ticks(&self, row: usize) -> Option<&UnsafeCell> {
+ self.ticks.get(row)
+ }
+
/// # Safety
/// index must be in-bounds
#[inline]
diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs
index 6f57b2dd0e4a6b..d064198c61aeef 100644
--- a/crates/bevy_ecs/src/system/system_param.rs
+++ b/crates/bevy_ecs/src/system/system_param.rs
@@ -369,9 +369,8 @@ unsafe impl SystemParamState for ResState {
);
combined_access.add_read(component_id);
- let resource_archetype = world.archetypes.resource();
- let archetype_component_id = resource_archetype
- .get_archetype_component_id(component_id)
+ let archetype_component_id = world
+ .get_resource_archetype_component_id(component_id)
.unwrap();
system_meta
.archetype_component_access
@@ -393,8 +392,8 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState {
world: &'w World,
change_tick: u32,
) -> Self::Item {
- let column = world
- .get_populated_resource_column(state.component_id)
+ let (ptr, ticks) = world
+ .get_resource_with_ticks(state.component_id)
.unwrap_or_else(|| {
panic!(
"Resource requested by {} does not exist: {}",
@@ -403,8 +402,8 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState {
)
});
Res {
- value: column.get_data_ptr().deref::(),
- ticks: column.get_ticks_unchecked(0).deref(),
+ value: ptr.deref(),
+ ticks: ticks.deref(),
last_change_tick: system_meta.last_change_tick,
change_tick,
}
@@ -442,10 +441,10 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState {
change_tick: u32,
) -> Self::Item {
world
- .get_populated_resource_column(state.0.component_id)
- .map(|column| Res {
- value: column.get_data_ptr().deref::(),
- ticks: column.get_ticks_unchecked(0).deref(),
+ .get_resource_with_ticks(state.0.component_id)
+ .map(|(ptr, ticks)| Res {
+ value: ptr.deref(),
+ ticks: ticks.deref(),
last_change_tick: system_meta.last_change_tick,
change_tick,
})
@@ -480,9 +479,8 @@ unsafe impl SystemParamState for ResMutState {
}
combined_access.add_write(component_id);
- let resource_archetype = world.archetypes.resource();
- let archetype_component_id = resource_archetype
- .get_archetype_component_id(component_id)
+ let archetype_component_id = world
+ .get_resource_archetype_component_id(component_id)
.unwrap();
system_meta
.archetype_component_access
@@ -938,9 +936,8 @@ unsafe impl SystemParamState for NonSendState {
);
combined_access.add_read(component_id);
- let resource_archetype = world.archetypes.resource();
- let archetype_component_id = resource_archetype
- .get_archetype_component_id(component_id)
+ let archetype_component_id = world
+ .get_resource_archetype_component_id(component_id)
.unwrap();
system_meta
.archetype_component_access
@@ -963,8 +960,8 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState {
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::();
- let column = world
- .get_populated_resource_column(state.component_id)
+ let (ptr, ticks) = world
+ .get_resource_with_ticks(state.component_id)
.unwrap_or_else(|| {
panic!(
"Non-send resource requested by {} does not exist: {}",
@@ -974,8 +971,8 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState {
});
NonSend {
- value: column.get_data_ptr().deref::(),
- ticks: column.get_ticks_unchecked(0).read(),
+ value: ptr.deref(),
+ ticks: ticks.read(),
last_change_tick: system_meta.last_change_tick,
change_tick,
}
@@ -1014,10 +1011,10 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState {
) -> Self::Item {
world.validate_non_send_access::();
world
- .get_populated_resource_column(state.0.component_id)
- .map(|column| NonSend {
- value: column.get_data_ptr().deref::(),
- ticks: column.get_ticks_unchecked(0).read(),
+ .get_resource_with_ticks(state.0.component_id)
+ .map(|(ptr, ticks)| NonSend {
+ value: ptr.deref(),
+ ticks: ticks.read(),
last_change_tick: system_meta.last_change_tick,
change_tick,
})
@@ -1054,9 +1051,8 @@ unsafe impl SystemParamState for NonSendMutState {
}
combined_access.add_write(component_id);
- let resource_archetype = world.archetypes.resource();
- let archetype_component_id = resource_archetype
- .get_archetype_component_id(component_id)
+ let archetype_component_id = world
+ .get_resource_archetype_component_id(component_id)
.unwrap();
system_meta
.archetype_component_access
@@ -1079,8 +1075,8 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState {
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::();
- let column = world
- .get_populated_resource_column(state.component_id)
+ let (ptr, ticks) = world
+ .get_resource_with_ticks(state.component_id)
.unwrap_or_else(|| {
panic!(
"Non-send resource requested by {} does not exist: {}",
@@ -1089,9 +1085,9 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState {
)
});
NonSendMut {
- value: column.get_data_ptr().assert_unique().deref_mut::(),
+ value: ptr.assert_unique().deref_mut(),
ticks: Ticks {
- component_ticks: column.get_ticks_unchecked(0).deref_mut(),
+ component_ticks: ticks.deref_mut(),
last_change_tick: system_meta.last_change_tick,
change_tick,
},
@@ -1128,11 +1124,11 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState {
) -> Self::Item {
world.validate_non_send_access::();
world
- .get_populated_resource_column(state.0.component_id)
- .map(|column| NonSendMut {
- value: column.get_data_ptr().assert_unique().deref_mut::(),
+ .get_resource_with_ticks(state.0.component_id)
+ .map(|(ptr, ticks)| NonSendMut {
+ value: ptr.assert_unique().deref_mut(),
ticks: Ticks {
- component_ticks: column.get_ticks_unchecked(0).deref_mut(),
+ component_ticks: ticks.deref_mut(),
last_change_tick: system_meta.last_change_tick,
change_tick,
},
diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs
index 3e45e8c100fea8..0a14e5e1405de1 100644
--- a/crates/bevy_ecs/src/world/mod.rs
+++ b/crates/bevy_ecs/src/world/mod.rs
@@ -8,22 +8,22 @@ pub use spawn_batch::*;
pub use world_cell::*;
use crate::{
- archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
+ archetype::{ArchetypeComponentId, ArchetypeId, Archetypes},
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::{MutUntyped, Ticks},
component::{
Component, ComponentDescriptor, ComponentId, ComponentInfo, ComponentTicks, Components,
- StorageType,
},
entity::{AllocAtWithoutReplacement, Entities, Entity},
query::{QueryState, ReadOnlyWorldQuery, WorldQuery},
- storage::{Column, SparseSet, Storages},
+ storage::{ResourceData, SparseSet, Storages},
system::Resource,
};
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use bevy_utils::tracing::debug;
use std::{
any::TypeId,
+ cell::UnsafeCell,
fmt,
sync::atomic::{AtomicU32, Ordering},
};
@@ -796,63 +796,37 @@ impl World {
#[allow(unused_unsafe)]
pub unsafe fn remove_resource_unchecked(&mut self) -> Option {
let component_id = self.components.get_resource_id(TypeId::of::())?;
- let resource_archetype = self.archetypes.resource_mut();
- let unique_components = resource_archetype.unique_components_mut();
- let column = unique_components.get_mut(component_id)?;
- if column.is_empty() {
- return None;
+ // SAFETY: the resource is of type R and the value is returned back to the caller.
+ unsafe {
+ let (ptr, _) = self.storages.resources.get_mut(component_id)?.remove()?;
+ Some(ptr.read::())
}
- // SAFETY: if a resource column exists, row 0 exists as well. caller takes ownership of the
- // ptr value / drop is called when R is dropped
- let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
- // SAFETY: column is of type R
- Some(unsafe { ptr.read::() })
}
/// Returns `true` if a resource of type `R` exists. Otherwise returns `false`.
#[inline]
pub fn contains_resource(&self) -> bool {
- let component_id =
- if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) {
- component_id
- } else {
- return false;
- };
- self.get_populated_resource_column(component_id).is_some()
+ self.components
+ .get_resource_id(TypeId::of::())
+ .and_then(|component_id| self.storages.resources.get(component_id))
+ .map(|info| info.is_present())
+ .unwrap_or(false)
}
pub fn is_resource_added(&self) -> bool {
- let component_id =
- if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) {
- component_id
- } else {
- return false;
- };
- let column = if let Some(column) = self.get_populated_resource_column(component_id) {
- column
- } else {
- return false;
- };
- // SAFETY: resources table always have row 0
- let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
- ticks.is_added(self.last_change_tick(), self.read_change_tick())
+ self.components
+ .get_resource_id(TypeId::of::())
+ .and_then(|component_id| self.storages.resources.get(component_id)?.get_ticks())
+ .map(|ticks| ticks.is_added(self.last_change_tick(), self.read_change_tick()))
+ .unwrap_or(false)
}
pub fn is_resource_changed(&self) -> bool {
- let component_id =
- if let Some(component_id) = self.components.get_resource_id(TypeId::of::()) {
- component_id
- } else {
- return false;
- };
- let column = if let Some(column) = self.get_populated_resource_column(component_id) {
- column
- } else {
- return false;
- };
- // SAFETY: resources table always have row 0
- let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
- ticks.is_changed(self.last_change_tick(), self.read_change_tick())
+ self.components
+ .get_resource_id(TypeId::of::())
+ .and_then(|component_id| self.storages.resources.get(component_id)?.get_ticks())
+ .map(|ticks| ticks.is_changed(self.last_change_tick(), self.read_change_tick()))
+ .unwrap_or(false)
}
/// Gets a reference to the resource of the given type
@@ -1013,6 +987,25 @@ impl World {
self.get_non_send_unchecked_mut_with_id(component_id)
}
+ // 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<'_>, &UnsafeCell)> {
+ self.storages.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(
+ &self,
+ component_id: ComponentId,
+ ) -> Option {
+ let resource = self.storages.resources.get(component_id)?;
+ Some(resource.id())
+ }
+
/// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given
/// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists).
/// This is faster than doing equivalent operations one-by-one.
@@ -1173,30 +1166,21 @@ impl World {
.components
.get_resource_id(TypeId::of::())
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::()));
-
// If the resource isn't send and sync, validate that we are on the main thread, so that we can access it.
let component_info = self.components().get_info(component_id).unwrap();
if !component_info.is_send_and_sync() {
self.validate_non_send_access::();
}
- let (ptr, mut ticks) = {
- let resource_archetype = self.archetypes.resource_mut();
- let unique_components = resource_archetype.unique_components_mut();
- let column = unique_components.get_mut(component_id).unwrap_or_else(|| {
- panic!("resource does not exist: {}", std::any::type_name::())
- });
- assert!(
- !column.is_empty(),
- "resource does not exist: {}",
- std::any::type_name::()
- );
- // SAFETY: if a resource column exists, row 0 exists as well. caller takes ownership of
- // the ptr value / drop is called when R is dropped
- unsafe { column.swap_remove_and_forget_unchecked(0) }
- };
- // SAFETY: pointer is of type R
+ let (ptr, mut ticks) = self
+ .storages
+ .resources
+ .get_mut(component_id)
+ // SAFETY: The type R is Send and Sync or we've already validated that we're on the main thread.
+ .and_then(|info| unsafe { info.remove() })
+ .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::()));
// Read the value onto the stack to avoid potential mut aliasing.
+ // SAFETY: pointer is of type R
let mut value = unsafe { ptr.read::() };
let value_mut = Mut {
value: &mut value,
@@ -1212,18 +1196,22 @@ impl World {
This is not allowed as the original resource is reinserted to the world after the FnOnce param is invoked.",
std::any::type_name::());
- let resource_archetype = self.archetypes.resource_mut();
- let unique_components = resource_archetype.unique_components_mut();
- let column = unique_components
- .get_mut(component_id)
- .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::()));
-
OwningPtr::make(value, |ptr| {
// SAFETY: pointer is of type R
unsafe {
- column.push(ptr, ticks);
+ self.storages
+ .resources
+ .get_mut(component_id)
+ .map(|info| info.insert_with_ticks(ptr, ticks))
+ .unwrap_or_else(|| {
+ panic!(
+ "No resource of type {} exists in the World.",
+ std::any::type_name::()
+ )
+ });
}
});
+
result
}
@@ -1258,8 +1246,11 @@ impl World {
&self,
component_id: ComponentId,
) -> Option<&R> {
- let column = self.get_populated_resource_column(component_id)?;
- Some(column.get_data_ptr().deref::())
+ self.storages
+ .resources
+ .get(component_id)?
+ .get_data()
+ .map(|ptr| ptr.deref())
}
/// # Safety
@@ -1270,11 +1261,11 @@ impl World {
&self,
component_id: ComponentId,
) -> Option> {
- let column = self.get_populated_resource_column(component_id)?;
+ let (ptr, ticks) = self.get_resource_with_ticks(component_id)?;
Some(Mut {
- value: column.get_data_ptr().assert_unique().deref_mut(),
+ value: ptr.assert_unique().deref_mut(),
ticks: Ticks {
- component_ticks: column.get_ticks_unchecked(0).deref_mut(),
+ component_ticks: ticks.deref_mut(),
last_change_tick: self.last_change_tick(),
change_tick: self.read_change_tick(),
},
@@ -1321,42 +1312,24 @@ impl World {
let change_tick = self.change_tick();
// SAFETY: component_id is valid, ensured by caller
- let column = self.initialize_resource_internal(component_id);
- if column.is_empty() {
- // SAFETY: column is of type R and has been allocated above
- column.push(value, ComponentTicks::new(change_tick));
- } else {
- column.replace(0, value, change_tick);
- }
+ self.initialize_resource_internal(component_id)
+ .insert(value, change_tick);
}
/// # Safety
/// `component_id` must be valid for this world
#[inline]
- unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
- // SAFETY: resource archetype always exists
- let resource_archetype = self
- .archetypes
- .archetypes
- .get_unchecked_mut(ArchetypeId::RESOURCE.index());
- let resource_archetype_components = &mut resource_archetype.components;
+ unsafe fn initialize_resource_internal(
+ &mut self,
+ component_id: ComponentId,
+ ) -> &mut ResourceData {
let archetype_component_count = &mut self.archetypes.archetype_component_count;
- let components = &self.components;
- resource_archetype
- .unique_components
- .get_or_insert_with(component_id, || {
- resource_archetype_components.insert(
- component_id,
- ArchetypeComponentInfo {
- archetype_component_id: ArchetypeComponentId::new(
- *archetype_component_count,
- ),
- storage_type: StorageType::Table,
- },
- );
+ self.storages
+ .resources
+ .initialize_with(component_id, &self.components, || {
+ let id = ArchetypeComponentId::new(*archetype_component_count);
*archetype_component_count += 1;
- let component_info = components.get_info_unchecked(component_id);
- Column::with_capacity(component_info, 1)
+ id
})
}
@@ -1374,22 +1347,6 @@ impl World {
component_id
}
- /// returns the resource column if the requested resource exists
- pub(crate) fn get_populated_resource_column(
- &self,
- component_id: ComponentId,
- ) -> Option<&Column> {
- let resource_archetype = self.archetypes.resource();
- let unique_components = resource_archetype.unique_components();
- unique_components.get(component_id).and_then(|column| {
- if column.is_empty() {
- None
- } else {
- Some(column)
- }
- })
- }
-
pub(crate) fn validate_non_send_access(&self) {
assert!(
self.main_thread_validator.is_main_thread(),
@@ -1449,10 +1406,7 @@ impl World {
let change_tick = self.change_tick();
self.storages.tables.check_change_ticks(change_tick);
self.storages.sparse_sets.check_change_ticks(change_tick);
- let resource_archetype = self.archetypes.resource_mut();
- for column in resource_archetype.unique_components.values_mut() {
- column.check_change_ticks(change_tick);
- }
+ self.storages.resources.check_change_ticks(change_tick);
}
pub fn clear_entities(&mut self) {
@@ -1476,9 +1430,7 @@ impl World {
if !info.is_send_and_sync() {
self.validate_non_send_access_untyped(info.name());
}
-
- let column = self.get_populated_resource_column(component_id)?;
- Some(column.get_data_ptr())
+ self.storages.resources.get(component_id)?.get_data()
}
/// Gets a resource to the resource with the id [`ComponentId`] if it exists.
@@ -1494,22 +1446,21 @@ impl World {
self.validate_non_send_access_untyped(info.name());
}
- let column = self.get_populated_resource_column(component_id)?;
+ let (ptr, ticks) = self.get_resource_with_ticks(component_id)?;
- // SAFETY: get_data_ptr requires that the mutability rules are not violated, and the caller promises
- // to only modify the resource while the mutable borrow of the world is valid
+ // SAFE: This function has exclusive access to the world so nothing aliases `ticks`.
let ticks = Ticks {
// SAFETY:
// - 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
- component_ticks: unsafe { &mut *column.get_ticks_unchecked(0).get() },
+ component_ticks: unsafe { ticks.deref_mut() },
last_change_tick: self.last_change_tick(),
change_tick: self.read_change_tick(),
};
Some(MutUntyped {
- // SAFETY: world access is unique, so no other reference can exist at the same time
- value: unsafe { column.get_data_ptr().assert_unique() },
+ // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`.
+ value: unsafe { ptr.assert_unique() },
ticks,
})
}
@@ -1523,16 +1474,13 @@ impl World {
if !info.is_send_and_sync() {
self.validate_non_send_access_untyped(info.name());
}
-
- let resource_archetype = self.archetypes.resource_mut();
- let unique_components = resource_archetype.unique_components_mut();
- let column = unique_components.get_mut(component_id)?;
- if column.is_empty() {
- return None;
+ // SAFETY: The underlying type is Send and Sync or we've already validated we're on the main thread
+ unsafe {
+ self.storages
+ .resources
+ .get_mut(component_id)?
+ .remove_and_drop();
}
- // SAFETY: if a resource column exists, row 0 exists as well
- unsafe { column.swap_remove_unchecked(0) };
-
Some(())
}
@@ -1586,10 +1534,7 @@ impl fmt::Debug for World {
.field("entity_count", &self.entities.len())
.field("archetype_count", &self.archetypes.len())
.field("component_count", &self.components.len())
- .field(
- "resource_count",
- &self.archetypes.resource().unique_components.len(),
- )
+ .field("resource_count", &self.storages.resources.len())
.finish()
}
}
diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs
index 9db47dfd8c0a2f..71c8216b249398 100644
--- a/crates/bevy_ecs/src/world/world_cell.rs
+++ b/crates/bevy_ecs/src/world/world_cell.rs
@@ -183,8 +183,9 @@ impl<'w> WorldCell<'w> {
/// Gets a reference to the resource of the given type
pub fn get_resource(&self) -> Option> {
let component_id = self.world.components.get_resource_id(TypeId::of::())?;
- let resource_archetype = self.world.archetypes.resource();
- let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
+ let archetype_component_id = self
+ .world
+ .get_resource_archetype_component_id(component_id)?;
Some(WorldBorrow::new(
// SAFETY: ComponentId matches TypeId
unsafe { self.world.get_resource_with_id(component_id)? },
@@ -215,8 +216,9 @@ 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 resource_archetype = self.world.archetypes.resource();
- let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
+ let archetype_component_id = self
+ .world
+ .get_resource_archetype_component_id(component_id)?;
Some(WorldBorrowMut::new(
// SAFETY: ComponentId matches TypeId and access is checked by WorldBorrowMut
unsafe {
@@ -250,8 +252,9 @@ 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 resource_archetype = self.world.archetypes.resource();
- let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
+ let archetype_component_id = self
+ .world
+ .get_resource_archetype_component_id(component_id)?;
Some(WorldBorrow::new(
// SAFETY: ComponentId matches TypeId
unsafe { self.world.get_non_send_with_id(component_id)? },
@@ -282,8 +285,9 @@ 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 resource_archetype = self.world.archetypes.resource();
- let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
+ let archetype_component_id = self
+ .world
+ .get_resource_archetype_component_id(component_id)?;
Some(WorldBorrowMut::new(
// SAFETY: ComponentId matches TypeId and access is checked by WorldBorrowMut
unsafe {
@@ -319,7 +323,7 @@ impl<'w> WorldCell<'w> {
mod tests {
use super::BASE_ACCESS;
use crate as bevy_ecs;
- use crate::{archetype::ArchetypeId, system::Resource, world::World};
+ use crate::{system::Resource, world::World};
use std::any::TypeId;
#[derive(Resource)]
@@ -377,10 +381,9 @@ mod tests {
}
}
- let resource_id = world.components.get_resource_id(TypeId::of::()).unwrap();
- let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap();
- let u32_archetype_component_id = resource_archetype
- .get_archetype_component_id(resource_id)
+ let u32_component_id = world.components.get_resource_id(TypeId::of::()).unwrap();
+ let u32_archetype_component_id = world
+ .get_resource_archetype_component_id(u32_component_id)
.unwrap();
assert_eq!(world.archetype_component_access.access.len(), 1);
assert_eq!(