diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index cf81b5b633a2e..9b9377e7ce2b9 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 90fd426e43d72..8dd9a7bf5e1c7 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 571f05184cdbd..6e848a042b492 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 0000000000000..edfa51e12857a --- /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 bdfbf51e0f9a5..d826b1c306575 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 6f57b2dd0e4a6..d064198c61aee 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 128c5e8dcf094..6ae591d355001 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}, }; @@ -786,63 +786,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 @@ -1003,6 +977,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. @@ -1163,30 +1156,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, @@ -1202,18 +1186,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 } @@ -1248,8 +1236,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 @@ -1260,11 +1251,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(), }, @@ -1311,42 +1302,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 }) } @@ -1364,22 +1337,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(), @@ -1439,10 +1396,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) { @@ -1466,9 +1420,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. @@ -1484,22 +1436,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, }) } @@ -1513,16 +1464,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(()) } @@ -1576,10 +1524,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 9db47dfd8c0a2..71c8216b24939 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!(