From 7cd3e3f2f301f6b99b0b93119cfcc3d2a2238902 Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 19 May 2022 17:36:25 -0700 Subject: [PATCH 01/37] Preliminary changes --- crates/bevy_ecs/src/archetype.rs | 14 +---- crates/bevy_ecs/src/storage/mod.rs | 3 + crates/bevy_ecs/src/storage/resource.rs | 36 ++++++++++++ crates/bevy_ecs/src/world/mod.rs | 74 ++++++++++--------------- 4 files changed, 70 insertions(+), 57 deletions(-) create mode 100644 crates/bevy_ecs/src/storage/resource.rs diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index ab33119a93be6..82883e683547d 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, @@ -122,7 +122,6 @@ pub struct Archetype { table_info: TableInfo, table_components: Box<[ComponentId]>, sparse_set_components: Box<[ComponentId]>, - pub(crate) unique_components: SparseSet, pub(crate) components: SparseSet, } @@ -170,7 +169,6 @@ impl Archetype { components, table_components, sparse_set_components, - unique_components: SparseSet::new(), entities: Default::default(), edges: Default::default(), } @@ -206,16 +204,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() diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index f54a2275bfe07..8a43f499f8c6a 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -3,9 +3,11 @@ mod blob_vec; mod sparse_set; mod table; +mod resource; pub use blob_vec::*; pub use sparse_set::*; +pub use resource::*; pub use table::*; /// The raw data stores of a [World](crate::world::World) @@ -13,4 +15,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..3f7894c910275 --- /dev/null +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -0,0 +1,36 @@ +use crate::component::ComponentId; +use crate::archetype::ArchetypeComponentInfo; +use crate::storage::{SparseSet, Column}; + +#[derive(Default)] +pub struct Resources { + resources: SparseSet, + components: SparseSet, +} + +impl Resources { + #[inline] + pub(crate) fn columns_mut(&mut self) -> impl Iterator { + self.resources.values_mut() + } + + #[inline] + pub(crate) fn components_mut(&mut self) -> &mut SparseSet { + &mut self.components + } + + #[inline] + pub fn len(&self) -> usize { + self.resources.len() + } + + #[inline] + pub(crate) fn get(&self, component: ComponentId) -> Option<&Column> { + self.resources.get(component) + } + + #[inline] + pub(crate) fn get_mut(&mut self, component: ComponentId) -> Option<&mut Column> { + self.resources.get_mut(component) + } +} diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 71ee4c7a3716b..ebc60459e0bab 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -681,9 +681,7 @@ 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)?; + let column = self.storages.resources.get_mut(component_id)?; if column.is_empty() { return None; } @@ -755,8 +753,8 @@ impl World { match self.get_resource() { Some(x) => x, None => panic!( - "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + "Requested resource {} does not exist in the `World`. + Did you forget to add it using `app.add_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -779,8 +777,8 @@ impl World { match self.get_resource_mut() { Some(x) => x, None => panic!( - "Requested resource {} does not exist in the `World`. - Did you forget to add it using `app.add_resource` / `app.init_resource`? + "Requested resource {} does not exist in the `World`. + Did you forget to add it using `app.add_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", std::any::type_name::() @@ -841,8 +839,8 @@ impl World { match self.get_non_send_resource() { Some(x) => x, None => panic!( - "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + "Requested non-send resource {} does not exist in the `World`. + Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), @@ -861,8 +859,8 @@ impl World { match self.get_non_send_resource_mut() { Some(x) => x, None => panic!( - "Requested non-send resource {} does not exist in the `World`. - Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? + "Requested non-send resource {} does not exist in the `World`. + Did you forget to add it using `app.add_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be be added by plugins.", std::any::type_name::() ), @@ -1053,9 +1051,7 @@ impl World { .get_resource_id(TypeId::of::()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); 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(|| { + let column = self.storages.resources.get_mut(component_id).unwrap_or_else(|| { panic!("resource does not exist: {}", std::any::type_name::()) }); assert!( @@ -1081,9 +1077,7 @@ impl World { let result = f(self, value_mut); assert!(!self.contains_resource::()); - let resource_archetype = self.archetypes.resource_mut(); - let unique_components = resource_archetype.unique_components_mut(); - let column = unique_components + let column = self.storages.resources .get_mut(component_id) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); @@ -1207,29 +1201,24 @@ impl World { #[inline] unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column { // SAFE: resource archetype always exists - let resource_archetype = self - .archetypes - .archetypes - .get_unchecked_mut(ArchetypeId::RESOURCE.index()); - let resource_archetype_components = &mut resource_archetype.components; + let resources = &mut self.storages.resources; + let resources_components = resources.components_mut(); 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, - }, - ); - *archetype_component_count += 1; - let component_info = components.get_info_unchecked(component_id); - Column::with_capacity(component_info, 1) - }) + resources.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, + }, + ); + *archetype_component_count += 1; + let component_info = components.get_info_unchecked(component_id); + Column::with_capacity(component_info, 1) + }) } pub(crate) fn initialize_resource(&mut self) -> ComponentId { @@ -1251,9 +1240,7 @@ impl World { &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| { + self.storages.resources.get(component_id).and_then(|column| { if column.is_empty() { None } else { @@ -1321,8 +1308,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() { + for column in self.storages.resources.columns_mut() { column.check_change_ticks(change_tick); } } @@ -1458,7 +1444,7 @@ impl fmt::Debug for World { .field("component_count", &self.components.len()) .field( "resource_count", - &self.archetypes.resource().unique_components.len(), + &self.storages.resources.len(), ) .finish() } From 76ede2f6d85db02a13d5a87376ebd98b2fed9762 Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 19 May 2022 22:57:15 -0700 Subject: [PATCH 02/37] Fix compilation --- crates/bevy_ecs/src/archetype.rs | 29 +------------- crates/bevy_ecs/src/lib.rs | 8 ++-- crates/bevy_ecs/src/storage/mod.rs | 4 +- crates/bevy_ecs/src/storage/resource.rs | 29 +++++++++----- crates/bevy_ecs/src/system/system_param.rs | 20 ++++++---- crates/bevy_ecs/src/world/mod.rs | 45 ++++++++++++---------- crates/bevy_ecs/src/world/world_cell.rs | 35 +++++++++++------ 7 files changed, 88 insertions(+), 82 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 82883e683547d..d7c5aed46a099 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -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] @@ -122,7 +121,7 @@ pub struct Archetype { table_info: TableInfo, table_components: Box<[ComponentId]>, sparse_set_components: Box<[ComponentId]>, - pub(crate) components: SparseSet, + components: SparseSet, } impl Archetype { @@ -362,17 +361,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 } } @@ -403,21 +391,6 @@ impl Archetypes { } } - #[inline] - pub fn resource(&self) -> &Archetype { - // SAFE: resource archetype always exists - unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) } - } - - #[inline] - pub fn resource_mut(&mut self) -> &mut Archetype { - // SAFE: 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 56ac5db9acd94..db1b27f0598f8 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1014,8 +1014,8 @@ mod tests { .get_resource_id(TypeId::of::()) .unwrap(); let archetype_component_id = world - .archetypes() - .resource() + .storages() + .resources .get_archetype_component_id(resource_id) .unwrap(); @@ -1081,8 +1081,8 @@ mod tests { ); let current_archetype_component_id = world - .archetypes() - .resource() + .storages() + .resources .get_archetype_component_id(current_resource_id) .unwrap(); diff --git a/crates/bevy_ecs/src/storage/mod.rs b/crates/bevy_ecs/src/storage/mod.rs index 8a43f499f8c6a..0764ad6046433 100644 --- a/crates/bevy_ecs/src/storage/mod.rs +++ b/crates/bevy_ecs/src/storage/mod.rs @@ -1,13 +1,13 @@ //! Storage layouts for ECS data. mod blob_vec; +mod resource; mod sparse_set; mod table; -mod resource; pub use blob_vec::*; -pub use sparse_set::*; pub use resource::*; +pub use sparse_set::*; pub use table::*; /// The raw data stores of a [World](crate::world::World) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 3f7894c910275..2b2fbbdebdedc 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,22 +1,28 @@ -use crate::component::ComponentId; +use crate::archetype::ArchetypeComponentId; use crate::archetype::ArchetypeComponentInfo; -use crate::storage::{SparseSet, Column}; +use crate::component::ComponentId; +use crate::storage::{Column, SparseSet}; #[derive(Default)] pub struct Resources { - resources: SparseSet, - components: SparseSet, + pub(crate) resources: SparseSet, + pub(crate) components: SparseSet, } impl Resources { #[inline] - pub(crate) fn columns_mut(&mut self) -> impl Iterator { + pub(crate) fn columns_mut(&mut self) -> impl Iterator { self.resources.values_mut() } #[inline] - pub(crate) fn components_mut(&mut self) -> &mut SparseSet { - &mut self.components + pub fn get_archetype_component_id( + &self, + component_id: ComponentId, + ) -> Option { + self.components + .get(component_id) + .map(|info| info.archetype_component_id) } #[inline] @@ -25,12 +31,17 @@ impl Resources { } #[inline] - pub(crate) fn get(&self, component: ComponentId) -> Option<&Column> { + pub fn is_empty(&self) -> bool { + self.resources.is_empty() + } + + #[inline] + pub(crate) fn get(&self, component: ComponentId) -> Option<&Column> { self.resources.get(component) } #[inline] - pub(crate) fn get_mut(&mut self, component: ComponentId) -> Option<&mut Column> { + pub(crate) fn get_mut(&mut self, component: ComponentId) -> Option<&mut Column> { self.resources.get_mut(component) } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 60c9c56ac7717..f9787c6db210b 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -323,8 +323,9 @@ unsafe impl SystemParamState for ResState { ); combined_access.add_read(component_id); - let resource_archetype = world.archetypes.resource(); - let archetype_component_id = resource_archetype + let archetype_component_id = world + .storages + .resources .get_archetype_component_id(component_id) .unwrap(); system_meta @@ -432,8 +433,9 @@ unsafe impl SystemParamState for ResMutState { } combined_access.add_write(component_id); - let resource_archetype = world.archetypes.resource(); - let archetype_component_id = resource_archetype + let archetype_component_id = world + .storages + .resources .get_archetype_component_id(component_id) .unwrap(); system_meta @@ -877,8 +879,9 @@ unsafe impl SystemParamState for NonSendState { ); combined_access.add_read(component_id); - let resource_archetype = world.archetypes.resource(); - let archetype_component_id = resource_archetype + let archetype_component_id = world + .storages + .resources .get_archetype_component_id(component_id) .unwrap(); system_meta @@ -991,8 +994,9 @@ unsafe impl SystemParamState for NonSendMutState { } combined_access.add_write(component_id); - let resource_archetype = world.archetypes.resource(); - let archetype_component_id = resource_archetype + let archetype_component_id = world + .storages + .resources .get_archetype_component_id(component_id) .unwrap(); system_meta diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ebc60459e0bab..2f66f483b6ef6 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1051,9 +1051,13 @@ impl World { .get_resource_id(TypeId::of::()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); let (ptr, mut ticks) = { - let column = self.storages.resources.get_mut(component_id).unwrap_or_else(|| { - panic!("resource does not exist: {}", std::any::type_name::()) - }); + let column = self + .storages + .resources + .get_mut(component_id) + .unwrap_or_else(|| { + panic!("resource does not exist: {}", std::any::type_name::()) + }); assert!( !column.is_empty(), "resource does not exist: {}", @@ -1077,7 +1081,9 @@ impl World { let result = f(self, value_mut); assert!(!self.contains_resource::()); - let column = self.storages.resources + let column = self + .storages + .resources .get_mut(component_id) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); @@ -1202,16 +1208,15 @@ impl World { unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column { // SAFE: resource archetype always exists let resources = &mut self.storages.resources; - let resources_components = resources.components_mut(); + let resource_components = &mut resources.components; + let resources = &mut resources.resources; let archetype_component_count = &mut self.archetypes.archetype_component_count; let components = &self.components; resources.get_or_insert_with(component_id, || { - resource_archetype_components.insert( + resource_components.insert( component_id, ArchetypeComponentInfo { - archetype_component_id: ArchetypeComponentId::new( - *archetype_component_count, - ), + archetype_component_id: ArchetypeComponentId::new(*archetype_component_count), storage_type: StorageType::Table, }, ); @@ -1240,13 +1245,16 @@ impl World { &self, component_id: ComponentId, ) -> Option<&Column> { - self.storages.resources.get(component_id).and_then(|column| { - if column.is_empty() { - None - } else { - Some(column) - } - }) + self.storages + .resources + .get(component_id) + .and_then(|column| { + if column.is_empty() { + None + } else { + Some(column) + } + }) } pub(crate) fn validate_non_send_access(&self) { @@ -1442,10 +1450,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.storages.resources.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 5a1fff6cdf9a2..3a6bd6e4eefc2 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -183,8 +183,11 @@ 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 + .storages + .resources + .get_archetype_component_id(component_id)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_resource_with_id(component_id)? }, @@ -215,8 +218,11 @@ 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 + .storages + .resources + .get_archetype_component_id(component_id)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -250,8 +256,11 @@ 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 + .storages + .resources + .get_archetype_component_id(component_id)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_non_send_with_id(component_id)? }, @@ -282,8 +291,11 @@ 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 + .storages + .resources + .get_archetype_component_id(component_id)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -318,7 +330,7 @@ impl<'w> WorldCell<'w> { #[cfg(test)] mod tests { use super::BASE_ACCESS; - use crate::{archetype::ArchetypeId, world::World}; + use crate::world::World; use std::any::TypeId; #[test] @@ -374,8 +386,9 @@ mod tests { .components .get_resource_id(TypeId::of::()) .unwrap(); - let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap(); - let u32_archetype_component_id = resource_archetype + let u32_archetype_component_id = world + .storages + .resources .get_archetype_component_id(u32_component_id) .unwrap(); assert_eq!(world.archetype_component_access.access.len(), 1); From b7b16be34ef0027b408a149eecd779f26061200c Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 19:28:29 -0700 Subject: [PATCH 03/37] Provide internal helper function --- crates/bevy_ecs/src/system/system_param.rs | 16 ++++------------ crates/bevy_ecs/src/world/mod.rs | 11 +++++++++++ crates/bevy_ecs/src/world/world_cell.rs | 20 +++++--------------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index f9787c6db210b..49bac5469c1b7 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -324,9 +324,7 @@ unsafe impl SystemParamState for ResState { combined_access.add_read(component_id); let archetype_component_id = world - .storages - .resources - .get_archetype_component_id(component_id) + .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access @@ -434,9 +432,7 @@ unsafe impl SystemParamState for ResMutState { combined_access.add_write(component_id); let archetype_component_id = world - .storages - .resources - .get_archetype_component_id(component_id) + .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access @@ -880,9 +876,7 @@ unsafe impl SystemParamState for NonSendState { combined_access.add_read(component_id); let archetype_component_id = world - .storages - .resources - .get_archetype_component_id(component_id) + .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access @@ -995,9 +989,7 @@ unsafe impl SystemParamState for NonSendMutState { combined_access.add_write(component_id); let archetype_component_id = world - .storages - .resources - .get_archetype_component_id(component_id) + .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 2f66f483b6ef6..5e63e6dbc4460 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -896,6 +896,17 @@ impl World { self.get_non_send_unchecked_mut_with_id(component_id) } + // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. + #[inline] + pub(crate) fn get_resource_archetype_component_id( + &self, + component_id: ComponentId, + ) -> Option { + self.storages + .resources + .get_archetype_component_id(component_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. diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index 3a6bd6e4eefc2..dfdaf27143b0b 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -185,9 +185,7 @@ impl<'w> WorldCell<'w> { let component_id = self.world.components.get_resource_id(TypeId::of::())?; let archetype_component_id = self .world - .storages - .resources - .get_archetype_component_id(component_id)?; + .get_resource_archetype_component_id(component_id)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_resource_with_id(component_id)? }, @@ -220,9 +218,7 @@ impl<'w> WorldCell<'w> { let component_id = self.world.components.get_resource_id(TypeId::of::())?; let archetype_component_id = self .world - .storages - .resources - .get_archetype_component_id(component_id)?; + .get_resource_archetype_component_id(component_id)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -258,9 +254,7 @@ impl<'w> WorldCell<'w> { let component_id = self.world.components.get_resource_id(TypeId::of::())?; let archetype_component_id = self .world - .storages - .resources - .get_archetype_component_id(component_id)?; + .get_resource_archetype_component_id(component_id)?; Some(WorldBorrow::new( // SAFE: ComponentId matches TypeId unsafe { self.world.get_non_send_with_id(component_id)? }, @@ -293,9 +287,7 @@ impl<'w> WorldCell<'w> { let component_id = self.world.components.get_resource_id(TypeId::of::())?; let archetype_component_id = self .world - .storages - .resources - .get_archetype_component_id(component_id)?; + .get_resource_archetype_component_id(component_id)?; Some(WorldBorrowMut::new( // SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut unsafe { @@ -387,9 +379,7 @@ mod tests { .get_resource_id(TypeId::of::()) .unwrap(); let u32_archetype_component_id = world - .storages - .resources - .get_archetype_component_id(u32_component_id) + .get_resource_archetype_component_id(u32_component_id) .unwrap(); assert_eq!(world.archetype_component_access.access.len(), 1); assert_eq!( From 5464d47f8244648b12e65fb75c4c1de82f8d453f Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 22:32:55 -0700 Subject: [PATCH 04/37] More complete Resources impl --- crates/bevy_ecs/src/storage/resource.rs | 93 ++++++++++++-- crates/bevy_ecs/src/system/system_param.rs | 60 +++++---- crates/bevy_ecs/src/world/mod.rs | 134 +++++++-------------- 3 files changed, 161 insertions(+), 126 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 2b2fbbdebdedc..1c0094bd76223 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,8 +1,14 @@ use crate::archetype::ArchetypeComponentId; use crate::archetype::ArchetypeComponentInfo; -use crate::component::ComponentId; +use crate::component::{ComponentId, ComponentTicks}; use crate::storage::{Column, SparseSet}; +use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; +use std::cell::UnsafeCell; +/// The backing store for all [`Resource`]s stored in the [`World`]. +/// +/// [`Resource`]: crate::system::system_param::Resource +/// [`World`]: crate::world::World #[derive(Default)] pub struct Resources { pub(crate) resources: SparseSet, @@ -10,11 +16,7 @@ pub struct Resources { } impl Resources { - #[inline] - pub(crate) fn columns_mut(&mut self) -> impl Iterator { - self.resources.values_mut() - } - + /// Gets the [`ArchetypeComponentId`] for a given resoruce. #[inline] pub fn get_archetype_component_id( &self, @@ -25,23 +27,94 @@ impl Resources { .map(|info| info.archetype_component_id) } + /// The total number of resoruces 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() } #[inline] - pub(crate) fn get(&self, component: ComponentId) -> Option<&Column> { - self.resources.get(component) + pub fn get(&self, component_id: ComponentId) -> Option> { + let column = self.resources.get(component_id)?; + // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the + // ptr value / drop is called when R is dropped + (!column.is_empty()).then(|| unsafe { column.get_data_unchecked(0) }) + } + + #[inline] + pub fn get_mut(&mut self, component_id: ComponentId) -> Option> { + let column = self.resources.get_mut(component_id)?; + // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the + // ptr value / drop is called when R is dropped + (!column.is_empty()).then(|| unsafe { column.get_data_unchecked_mut(0) }) + } + + #[inline] + pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { + let column = self.resources.get(component_id)?; + // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the + // ptr value / drop is called when R is dropped + (!column.is_empty()).then(|| unsafe { column.get_ticks_unchecked(0).deref() }) + } + + #[inline] + pub fn contains(&self, component_id: ComponentId) -> bool { + self.resources.contains(component_id) } #[inline] - pub(crate) fn get_mut(&mut self, component: ComponentId) -> Option<&mut Column> { - self.resources.get_mut(component) + pub(crate) fn get_with_ticks_unchecked( + &self, + component_id: ComponentId, + ) -> Option<(Ptr<'_>, &UnsafeCell)> { + let column = self.resources.get(component_id)?; + // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the + // ptr value / drop is called when R is dropped + (!column.is_empty()) + .then(|| unsafe { (column.get_data_unchecked(0), column.get_ticks_unchecked(0)) }) + } + + // # Safety + // - ptr must point to valid data of this column's component type + #[inline] + pub unsafe fn insert( + &mut self, + component_id: ComponentId, + data: OwningPtr<'_>, + ticks: ComponentTicks, + ) -> Option<()> { + let column = self.resources.get_mut(component_id)?; + debug_assert!(column.is_empty()); + column.push(data, ticks); + Some(()) + } + + #[inline] + #[must_use = "The returned pointer to the removed component should be used or dropped"] + pub fn remove(&mut self, component_id: ComponentId) -> Option<(OwningPtr<'_>, ComponentTicks)> { + let column = self.resources.get_mut(component_id)?; + if column.is_empty() { + return None; + } + // SAFE: 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 { Some(column.swap_remove_and_forget_unchecked(0)) } + } + + pub fn check_change_ticks(&mut self, change_tick: u32) { + for column in self.storages.resources.columns_mut() { + column.check_change_ticks(change_tick); + } } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 49bac5469c1b7..04f5bc61caf77 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -346,8 +346,10 @@ 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 + .storages() + .resources + .get_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -356,8 +358,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, } @@ -393,10 +395,12 @@ 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(), + .storages() + .resources + .get_with_ticks_unchecked(state.0.component_id) + .map(|(ptr, ticks)| Res { + value: ptr.deref(), + ticks: ticks.deref(), last_change_tick: system_meta.last_change_tick, change_tick, }) @@ -899,8 +903,10 @@ 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 + .storages() + .resources + .get_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -910,8 +916,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, } @@ -948,10 +954,12 @@ 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(), + .storages() + .resources + .get_with_ticks_unchecked(state.0.component_id) + .map(|(ptr, ticks)| NonSend { + value: ptr.deref(), + ticks: ticks.read(), last_change_tick: system_meta.last_change_tick, change_tick, }) @@ -1012,8 +1020,10 @@ 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 + .storages() + .resources + .get_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -1022,9 +1032,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, }, @@ -1059,11 +1069,13 @@ 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::(), + .storages() + .resources + .get_with_ticks_unchecked(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 5e63e6dbc4460..47664d10a51da 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -681,61 +681,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 column = self.storages.resources.get_mut(component_id)?; - if column.is_empty() { - return None; + // SAFE: the resource is of type R and the value is returned back to the caller. + // The access here must not be used on non-send types. + unsafe { + let (ptr, _) = self.storages.resources.remove(component_id)?; + Some(ptr.read::()) } - // SAFE: 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) }; - // SAFE: 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::()) + .map(|component_id| self.storages.resources.contains(component_id)) + .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; - }; - // SAFE: 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_ticks(component_id)) + .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; - }; - // SAFE: 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_ticks(component_id)) + .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 @@ -1061,23 +1037,11 @@ impl World { .components .get_resource_id(TypeId::of::()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); - let (ptr, mut ticks) = { - let column = self - .storages - .resources - .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::() - ); - // SAFE: 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) } - }; + let (ptr, mut ticks) = self + .storages + .resources + .remove(component_id) + .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); // SAFE: pointer is of type R // Read the value onto the stack to avoid potential mut aliasing. let mut value = unsafe { ptr.read::() }; @@ -1092,18 +1056,18 @@ impl World { let result = f(self, value_mut); assert!(!self.contains_resource::()); - let column = self - .storages - .resources - .get_mut(component_id) - .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); - OwningPtr::make(value, |ptr| { unsafe { // SAFE: pointer is of type R - column.push(ptr, ticks); + self.storages + .resources + .insert(component_id, ptr, ticks) + .unwrap_or_else(|| { + panic!("resource does not exist: {}", std::any::type_name::()) + }); } }); + result } @@ -1114,8 +1078,10 @@ 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) + .map(|ptr| ptr.deref()) } /// # Safety @@ -1126,11 +1092,14 @@ impl World { &self, component_id: ComponentId, ) -> Option> { - let column = self.get_populated_resource_column(component_id)?; + let (ptr, ticks) = self + .storages + .resources + .get_with_ticks_unchecked(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(), }, @@ -1251,23 +1220,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> { - self.storages - .resources - .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(), @@ -1327,9 +1279,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); - for column in self.storages.resources.columns_mut() { - column.check_change_ticks(change_tick); - } + self.storages.resources.check_change_ticks(change_tick); } pub fn clear_entities(&mut self) { From 32c5456859ee2f69cbd19e66f98175d818018f38 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 22:45:25 -0700 Subject: [PATCH 05/37] Fix build --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 1c0094bd76223..3a4a53620e709 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -113,7 +113,7 @@ impl Resources { } pub fn check_change_ticks(&mut self, change_tick: u32) { - for column in self.storages.resources.columns_mut() { + for column in self.resources.values_mut() { column.check_change_ticks(change_tick); } } From bb9a49fc70d7ad3df1e3f2196b2cc588574136a5 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 22:52:05 -0700 Subject: [PATCH 06/37] Cleanup --- crates/bevy_ecs/src/system/system_param.rs | 24 ++++++---------------- crates/bevy_ecs/src/world/mod.rs | 16 +++++++++++---- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 04f5bc61caf77..2049837b1aaed 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -347,9 +347,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState { change_tick: u32, ) -> Self::Item { let (ptr, ticks) = world - .storages() - .resources - .get_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -395,9 +393,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState { change_tick: u32, ) -> Self::Item { world - .storages() - .resources - .get_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks_unchecked(state.0.component_id) .map(|(ptr, ticks)| Res { value: ptr.deref(), ticks: ticks.deref(), @@ -904,9 +900,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState { ) -> Self::Item { world.validate_non_send_access::(); let (ptr, ticks) = world - .storages() - .resources - .get_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -954,9 +948,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState { ) -> Self::Item { world.validate_non_send_access::(); world - .storages() - .resources - .get_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks_unchecked(state.0.component_id) .map(|(ptr, ticks)| NonSend { value: ptr.deref(), ticks: ticks.read(), @@ -1021,9 +1013,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState { ) -> Self::Item { world.validate_non_send_access::(); let (ptr, ticks) = world - .storages() - .resources - .get_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks_unchecked(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -1069,9 +1059,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState { ) -> Self::Item { world.validate_non_send_access::(); world - .storages() - .resources - .get_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks_unchecked(state.0.component_id) .map(|(ptr, ticks)| NonSendMut { value: ptr.assert_unique().deref_mut(), ticks: Ticks { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 47664d10a51da..342fd8316c893 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -872,6 +872,17 @@ 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_unchecked( + &self, + component_id: ComponentId, + ) -> Option<(Ptr<'_>, &UnsafeCell)> { + self.storages + .resources + .get_with_ticks_unchecked(component_id) + } + // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. #[inline] pub(crate) fn get_resource_archetype_component_id( @@ -1092,10 +1103,7 @@ impl World { &self, component_id: ComponentId, ) -> Option> { - let (ptr, ticks) = self - .storages - .resources - .get_with_ticks_unchecked(component_id)?; + let (ptr, ticks) = self.get_resource_with_ticks_unchecked(component_id)?; Some(Mut { value: ptr.assert_unique().deref_mut(), ticks: Ticks { From b719c1bbd4313a818cccccefef64a34cd8bb07f6 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 30 May 2022 23:13:59 -0700 Subject: [PATCH 07/37] Fix CI --- crates/bevy_ecs/src/storage/resource.rs | 11 +++++++---- crates/bevy_ecs/src/world/mod.rs | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 3a4a53620e709..4679ff84db23a 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -7,7 +7,7 @@ use std::cell::UnsafeCell; /// The backing store for all [`Resource`]s stored in the [`World`]. /// -/// [`Resource`]: crate::system::system_param::Resource +/// [`Resource`]: crate::system::Resource /// [`World`]: crate::world::World #[derive(Default)] pub struct Resources { @@ -70,7 +70,10 @@ impl Resources { #[inline] pub fn contains(&self, component_id: ComponentId) -> bool { - self.resources.contains(component_id) + self.resources + .get(component_id) + .map(|column| !column.is_empty()) + .unwrap_or(false) } #[inline] @@ -85,8 +88,8 @@ impl Resources { .then(|| unsafe { (column.get_data_unchecked(0), column.get_ticks_unchecked(0)) }) } - // # Safety - // - ptr must point to valid data of this column's component type + /// # Safety + /// - ptr must point to valid data of this column's component type #[inline] pub unsafe fn insert( &mut self, diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 342fd8316c893..a5da40825d93e 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -23,6 +23,7 @@ use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; use bevy_utils::tracing::debug; use std::{ any::TypeId, + cell::UnsafeCell, fmt, sync::atomic::{AtomicU32, Ordering}, }; From 99f14d703d001fe257f56b8f2af28a984c61c65d Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 7 Jun 2022 01:47:25 -0700 Subject: [PATCH 08/37] Fix CI --- crates/bevy_ecs/src/storage/resource.rs | 16 +++++++++- crates/bevy_ecs/src/system/system_param.rs | 12 ++++---- crates/bevy_ecs/src/world/mod.rs | 36 ++++++---------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 4679ff84db23a..ebf98aef63497 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -77,7 +77,7 @@ impl Resources { } #[inline] - pub(crate) fn get_with_ticks_unchecked( + pub(crate) fn get_with_ticks( &self, component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { @@ -115,6 +115,20 @@ impl Resources { unsafe { Some(column.swap_remove_and_forget_unchecked(0)) } } + #[inline] + pub(crate) fn remove_and_drop(&mut self, component_id: ComponentId) -> Option<()> { + let column = self.resources.get_mut(component_id)?; + if column.is_empty() { + return None; + } + // SAFE: if a resource column exists, row 0 exists as well. The removed value is dropped + // immediately. + unsafe { + column.swap_remove_unchecked(0); + Some(()) + } + } + pub fn check_change_ticks(&mut self, change_tick: u32) { for column in self.resources.values_mut() { column.check_change_ticks(change_tick); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 2049837b1aaed..5b46dafe55aaa 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -347,7 +347,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for ResState { change_tick: u32, ) -> Self::Item { let (ptr, ticks) = world - .get_resource_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks(state.component_id) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -393,7 +393,7 @@ impl<'w, 's, T: Resource> SystemParamFetch<'w, 's> for OptionResState { change_tick: u32, ) -> Self::Item { world - .get_resource_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks(state.0.component_id) .map(|(ptr, ticks)| Res { value: ptr.deref(), ticks: ticks.deref(), @@ -900,7 +900,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState { ) -> Self::Item { world.validate_non_send_access::(); let (ptr, ticks) = world - .get_resource_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -948,7 +948,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState { ) -> Self::Item { world.validate_non_send_access::(); world - .get_resource_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks(state.0.component_id) .map(|(ptr, ticks)| NonSend { value: ptr.deref(), ticks: ticks.read(), @@ -1013,7 +1013,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState { ) -> Self::Item { world.validate_non_send_access::(); let (ptr, ticks) = world - .get_resource_with_ticks_unchecked(state.component_id) + .get_resource_with_ticks(state.component_id) .unwrap_or_else(|| { panic!( "Non-send resource requested by {} does not exist: {}", @@ -1059,7 +1059,7 @@ impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState { ) -> Self::Item { world.validate_non_send_access::(); world - .get_resource_with_ticks_unchecked(state.0.component_id) + .get_resource_with_ticks(state.0.component_id) .map(|(ptr, ticks)| NonSendMut { value: ptr.assert_unique().deref_mut(), ticks: Ticks { diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index a5da40825d93e..fdbf8211a1253 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -875,13 +875,11 @@ impl World { // Shorthand helper function for getting the data and change ticks for a resource. #[inline] - pub(crate) fn get_resource_with_ticks_unchecked( + pub(crate) fn get_resource_with_ticks( &self, component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { - self.storages - .resources - .get_with_ticks_unchecked(component_id) + self.storages.resources.get_with_ticks(component_id) } // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. @@ -1104,7 +1102,7 @@ impl World { &self, component_id: ComponentId, ) -> Option> { - let (ptr, ticks) = self.get_resource_with_ticks_unchecked(component_id)?; + let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; Some(Mut { value: ptr.assert_unique().deref_mut(), ticks: Ticks { @@ -1312,9 +1310,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) } /// Gets a resource to the resource with the id [`ComponentId`] if it exists. @@ -1330,20 +1326,18 @@ 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)?; - // SAFE: 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 funtion xclusive access to the world. The ptr must have unique access. let ticks = 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 - 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 { - value: unsafe { column.get_data_ptr().assert_unique() }, + // SAFE: This funtion xclusive access to the world. The ptr must have unique access. + value: unsafe { ptr.assert_unique() }, ticks, }) } @@ -1357,17 +1351,7 @@ 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; - } - // SAFE: if a resource column exists, row 0 exists as well - unsafe { column.swap_remove_unchecked(0) }; - - Some(()) + self.storages.resources.remove_and_drop(component_id) } /// Retrieves a mutable untyped reference to the given `entity`'s [Component] of the given [`ComponentId`]. From 4918e10c35410618752c0babd954752cf18a909c Mon Sep 17 00:00:00 2001 From: james7132 Date: Tue, 7 Jun 2022 02:10:05 -0700 Subject: [PATCH 09/37] Shrink Resources --- crates/bevy_ecs/src/storage/resource.rs | 46 +++++++++++++++++-------- crates/bevy_ecs/src/world/mod.rs | 31 +++++++++-------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index ebf98aef63497..4a1603b1e01f3 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -5,14 +5,18 @@ use crate::storage::{Column, SparseSet}; use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; use std::cell::UnsafeCell; +pub(crate) struct ResourceInfo { + pub data: Column, + pub component_info: ArchetypeComponentInfo, +} + /// The backing store for all [`Resource`]s stored in the [`World`]. /// /// [`Resource`]: crate::system::Resource /// [`World`]: crate::world::World #[derive(Default)] pub struct Resources { - pub(crate) resources: SparseSet, - pub(crate) components: SparseSet, + pub(crate) resources: SparseSet, } impl Resources { @@ -22,9 +26,9 @@ impl Resources { &self, component_id: ComponentId, ) -> Option { - self.components + self.resources .get(component_id) - .map(|info| info.archetype_component_id) + .map(|info| info.component_info.archetype_component_id) } /// The total number of resoruces stored in the [`World`] @@ -44,35 +48,39 @@ impl Resources { self.resources.is_empty() } + /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] pub fn get(&self, component_id: ComponentId) -> Option> { - let column = self.resources.get(component_id)?; + let column = &self.resources.get(component_id)?.data; // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the // ptr value / drop is called when R is dropped (!column.is_empty()).then(|| unsafe { column.get_data_unchecked(0) }) } + /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] pub fn get_mut(&mut self, component_id: ComponentId) -> Option> { - let column = self.resources.get_mut(component_id)?; + let column = &mut self.resources.get_mut(component_id)?.data; // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the // ptr value / drop is called when R is dropped (!column.is_empty()).then(|| unsafe { column.get_data_unchecked_mut(0) }) } + /// Gets the [`ComponentTicks`] to a resource, if available. #[inline] pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { - let column = self.resources.get(component_id)?; + let column = &self.resources.get(component_id)?.data; // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the // ptr value / drop is called when R is dropped (!column.is_empty()).then(|| unsafe { column.get_ticks_unchecked(0).deref() }) } + /// Checks if the a resource is currently stored with a given ID. #[inline] pub fn contains(&self, component_id: ComponentId) -> bool { self.resources .get(component_id) - .map(|column| !column.is_empty()) + .map(|info| !info.data.is_empty()) .unwrap_or(false) } @@ -81,15 +89,18 @@ impl Resources { &self, component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { - let column = self.resources.get(component_id)?; + let column = &self.resources.get(component_id)?.data; // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the // ptr value / drop is called when R is dropped (!column.is_empty()) .then(|| unsafe { (column.get_data_unchecked(0), column.get_ticks_unchecked(0)) }) } + /// Inserts a resource into the world. + /// /// # Safety - /// - ptr must point to valid data of this column's component type + /// ptr must point to valid data of this column's component type which + /// must correspond to the provided ID. #[inline] pub unsafe fn insert( &mut self, @@ -97,16 +108,21 @@ impl Resources { data: OwningPtr<'_>, ticks: ComponentTicks, ) -> Option<()> { - let column = self.resources.get_mut(component_id)?; + let column = &mut self.resources.get_mut(component_id)?.data; debug_assert!(column.is_empty()); column.push(data, ticks); Some(()) } + /// Removes a resource from the world. + /// + /// # Safety + /// ptr must point to valid data of this column's component type which + /// must correspond to the provided ID. #[inline] #[must_use = "The returned pointer to the removed component should be used or dropped"] pub fn remove(&mut self, component_id: ComponentId) -> Option<(OwningPtr<'_>, ComponentTicks)> { - let column = self.resources.get_mut(component_id)?; + let column = &mut self.resources.get_mut(component_id)?.data; if column.is_empty() { return None; } @@ -117,7 +133,7 @@ impl Resources { #[inline] pub(crate) fn remove_and_drop(&mut self, component_id: ComponentId) -> Option<()> { - let column = self.resources.get_mut(component_id)?; + let column = &mut self.resources.get_mut(component_id)?.data; if column.is_empty() { return None; } @@ -130,8 +146,8 @@ impl Resources { } pub fn check_change_ticks(&mut self, change_tick: u32) { - for column in self.resources.values_mut() { - column.check_change_ticks(change_tick); + for info in self.resources.values_mut() { + info.data.check_change_ticks(change_tick); } } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index fdbf8211a1253..1f1c3541e1add 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -16,7 +16,7 @@ use crate::{ }, entity::{AllocAtWithoutReplacement, Entities, Entity}, query::{QueryState, WorldQuery}, - storage::{Column, SparseSet, Storages}, + storage::{Column, ResourceInfo, SparseSet, Storages}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; @@ -1195,22 +1195,25 @@ impl World { unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column { // SAFE: resource archetype always exists let resources = &mut self.storages.resources; - let resource_components = &mut resources.components; let resources = &mut resources.resources; let archetype_component_count = &mut self.archetypes.archetype_component_count; let components = &self.components; - resources.get_or_insert_with(component_id, || { - resource_components.insert( - component_id, - ArchetypeComponentInfo { - archetype_component_id: ArchetypeComponentId::new(*archetype_component_count), - storage_type: StorageType::Table, - }, - ); - *archetype_component_count += 1; - let component_info = components.get_info_unchecked(component_id); - Column::with_capacity(component_info, 1) - }) + &mut resources + .get_or_insert_with(component_id, || { + let component_info = components.get_info_unchecked(component_id); + let info = ResourceInfo { + data: Column::with_capacity(component_info, 1), + component_info: ArchetypeComponentInfo { + archetype_component_id: ArchetypeComponentId::new( + *archetype_component_count, + ), + storage_type: StorageType::Table, + }, + }; + *archetype_component_count += 1; + info + }) + .data } pub(crate) fn initialize_resource(&mut self) -> ComponentId { From 2b24bac19634d943fc099f696f9529bef3e389f3 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 11 Jun 2022 15:54:06 -0700 Subject: [PATCH 10/37] Less jank way of fetching ticks. Co-authored-by: Boxy --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 4a1603b1e01f3..9c56056629302 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -72,7 +72,7 @@ impl Resources { let column = &self.resources.get(component_id)?.data; // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the // ptr value / drop is called when R is dropped - (!column.is_empty()).then(|| unsafe { column.get_ticks_unchecked(0).deref() }) + column.get_ticks_slice().get(0).map(|ticks| unsafe { ticks.deref() }) } /// Checks if the a resource is currently stored with a given ID. From ab675d1e3bf64742d218b7b7d25328c244c93763 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 11 Jun 2022 15:55:27 -0700 Subject: [PATCH 11/37] Better safety comment. Co-authored-by: Boxy --- crates/bevy_ecs/src/world/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 1f1c3541e1add..974cfcd367e47 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1331,7 +1331,7 @@ impl World { let (ptr, ticks) = self.get_resource_with_ticks(component_id)?; - // SAFE: This funtion xclusive access to the world. The ptr must have unique access. + // SAFE: This function has exclusive access to the world so nothing aliases `ticks`. let ticks = Ticks { component_ticks: unsafe { ticks.deref_mut() }, last_change_tick: self.last_change_tick(), From bd25ccc4c90ad642be9fd85a02acfb2966a965b4 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 11 Jun 2022 15:55:35 -0700 Subject: [PATCH 12/37] Better safety comment. Co-authored-by: Boxy --- crates/bevy_ecs/src/world/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 974cfcd367e47..ecb9659fd3b7b 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1339,7 +1339,7 @@ impl World { }; Some(MutUntyped { - // SAFE: This funtion xclusive access to the world. The ptr must have unique access. + // SAFE: This function has exclusive access to the world so nothing aliases `ptr`. value: unsafe { ptr.assert_unique() }, ticks, }) From 70578fe6806f7b4a5d24a85638adc00ddbd1d28e Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 11 Jun 2022 15:56:24 -0700 Subject: [PATCH 13/37] Correct safety comment pointing to the right parameter. Co-authored-by: Boxy --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 9c56056629302..6e026e3405525 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -99,7 +99,7 @@ impl Resources { /// Inserts a resource into the world. /// /// # Safety - /// ptr must point to valid data of this column's component type which + /// `data` must point to valid data of this column's component type which /// must correspond to the provided ID. #[inline] pub unsafe fn insert( From 27ce1a361d8a175378c3cac4767f99954eae2ed3 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 11 Jun 2022 15:56:56 -0700 Subject: [PATCH 14/37] Much simpler remove impl. Co-authored-by: Boxy --- crates/bevy_ecs/src/storage/resource.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 6e026e3405525..f55447efb39d5 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -134,15 +134,7 @@ impl Resources { #[inline] pub(crate) fn remove_and_drop(&mut self, component_id: ComponentId) -> Option<()> { let column = &mut self.resources.get_mut(component_id)?.data; - if column.is_empty() { - return None; - } - // SAFE: if a resource column exists, row 0 exists as well. The removed value is dropped - // immediately. - unsafe { - column.swap_remove_unchecked(0); - Some(()) - } + (column.len() > 0).then(|| column.clear()) } pub fn check_change_ticks(&mut self, change_tick: u32) { From 520fd3062586298e76c2a46d72001da74f2a20e9 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 03:22:19 -0700 Subject: [PATCH 15/37] Update safety comments --- crates/bevy_ecs/src/storage/resource.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index f55447efb39d5..7731bceadf3b8 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -52,8 +52,7 @@ impl Resources { #[inline] pub fn get(&self, component_id: ComponentId) -> Option> { let column = &self.resources.get(component_id)?.data; - // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the - // ptr value / drop is called when R is dropped + // SAFE: This checks that the column is not empty, so row 0 must exist. (!column.is_empty()).then(|| unsafe { column.get_data_unchecked(0) }) } @@ -61,8 +60,7 @@ impl Resources { #[inline] pub fn get_mut(&mut self, component_id: ComponentId) -> Option> { let column = &mut self.resources.get_mut(component_id)?.data; - // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the - // ptr value / drop is called when R is dropped + // SAFE: This checks that the column is not empty, so row 0 must exist. (!column.is_empty()).then(|| unsafe { column.get_data_unchecked_mut(0) }) } @@ -70,8 +68,7 @@ impl Resources { #[inline] pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { let column = &self.resources.get(component_id)?.data; - // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the - // ptr value / drop is called when R is dropped + // SAFE: If row 0exists, it's ComponentTicks must be valid as well. column.get_ticks_slice().get(0).map(|ticks| unsafe { ticks.deref() }) } @@ -90,8 +87,7 @@ impl Resources { component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { let column = &self.resources.get(component_id)?.data; - // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the - // ptr value / drop is called when R is dropped + // SAFE: This checks that the column is not empty, so row 0 must exist. (!column.is_empty()) .then(|| unsafe { (column.get_data_unchecked(0), column.get_ticks_unchecked(0)) }) } @@ -126,8 +122,8 @@ impl Resources { if column.is_empty() { return None; } - // SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the - // ptr value / drop is called when R is dropped + // SAFE: This checks that the column is not empty, so row 0 must exist. caller takes ownership + // of the ptr value / drop is called when R is dropped unsafe { Some(column.swap_remove_and_forget_unchecked(0)) } } From 7737705486613a5412a5bdf65101d07f6d819ad8 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 03:31:18 -0700 Subject: [PATCH 16/37] Move the unsafe into Column --- crates/bevy_ecs/src/storage/resource.rs | 13 ++++--------- crates/bevy_ecs/src/storage/table.rs | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 7731bceadf3b8..9651aafc9e5a6 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -51,25 +51,20 @@ impl Resources { /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] pub fn get(&self, component_id: ComponentId) -> Option> { - let column = &self.resources.get(component_id)?.data; - // SAFE: This checks that the column is not empty, so row 0 must exist. - (!column.is_empty()).then(|| unsafe { column.get_data_unchecked(0) }) + self.resources.get(component_id)?.data.get_data(0) } /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] pub fn get_mut(&mut self, component_id: ComponentId) -> Option> { - let column = &mut self.resources.get_mut(component_id)?.data; - // SAFE: This checks that the column is not empty, so row 0 must exist. - (!column.is_empty()).then(|| unsafe { column.get_data_unchecked_mut(0) }) + self.resources.get_mut(component_id)?.data.get_data_mut(0) } /// Gets the [`ComponentTicks`] to a resource, if available. #[inline] pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { - let column = &self.resources.get(component_id)?.data; - // SAFE: If row 0exists, it's ComponentTicks must be valid as well. - column.get_ticks_slice().get(0).map(|ticks| unsafe { ticks.deref() }) + // SAFE: If row 0 exists, it's ComponentTicks must be valid as well. + self.resources.get(component_id)?.data.get_ticks(0).map(|ticks| unsafe { ticks.deref() }) } /// Checks if the a resource is currently stored with a given ID. diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index a571a631d66c2..e365fdb071c2e 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -147,6 +147,13 @@ impl Column { &self.ticks } + #[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 @@ -156,6 +163,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 @@ -165,6 +179,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] From a6b5e4bdd54caeefeeb457273052c3335e0557eb Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 03:35:10 -0700 Subject: [PATCH 17/37] Column::get --- crates/bevy_ecs/src/storage/resource.rs | 5 +---- crates/bevy_ecs/src/storage/table.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 9651aafc9e5a6..efaca8050fe52 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -81,10 +81,7 @@ impl Resources { &self, component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { - let column = &self.resources.get(component_id)?.data; - // SAFE: This checks that the column is not empty, so row 0 must exist. - (!column.is_empty()) - .then(|| unsafe { (column.get_data_unchecked(0), column.get_ticks_unchecked(0)) }) + self.resources.get(component_id)?.data.get(0) } /// Inserts a resource into the world. diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index e365fdb071c2e..f7a43029aba77 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -147,6 +147,19 @@ impl Column { &self.ticks } + #[inline] + pub fn get(&self, row: usize) -> Option<(Ptr<'_>, &UnsafeCell)> { + // 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), + 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 From 610c617543b0b3122064c756d9e1224c78a4686c Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 03:43:29 -0700 Subject: [PATCH 18/37] Formatting --- crates/bevy_ecs/src/storage/resource.rs | 17 ++++++++------- crates/bevy_ecs/src/storage/table.rs | 29 +++++++++++++++++-------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index efaca8050fe52..095631c7094f7 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -64,7 +64,11 @@ impl Resources { #[inline] pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { // SAFE: If row 0 exists, it's ComponentTicks must be valid as well. - self.resources.get(component_id)?.data.get_ticks(0).map(|ticks| unsafe { ticks.deref() }) + self.resources + .get(component_id)? + .data + .get_ticks(0) + .map(|ticks| unsafe { ticks.deref() }) } /// Checks if the a resource is currently stored with a given ID. @@ -110,13 +114,10 @@ impl Resources { #[inline] #[must_use = "The returned pointer to the removed component should be used or dropped"] pub fn remove(&mut self, component_id: ComponentId) -> Option<(OwningPtr<'_>, ComponentTicks)> { - let column = &mut self.resources.get_mut(component_id)?.data; - if column.is_empty() { - return None; - } - // SAFE: This checks that the column is not empty, so row 0 must exist. caller takes ownership - // of the ptr value / drop is called when R is dropped - unsafe { Some(column.swap_remove_and_forget_unchecked(0)) } + self.resources + .get_mut(component_id)? + .data + .swap_remove_and_forget(0) } #[inline] diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index f7a43029aba77..965874e4e6df1 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -107,6 +107,22 @@ impl Column { self.ticks.swap_remove(row); } + #[inline] + #[must_use = "The returned pointer should be used to dropped 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( @@ -151,20 +167,15 @@ impl Column { pub fn get(&self, row: usize) -> Option<(Ptr<'_>, &UnsafeCell)> { // 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), - self.ticks.get_unchecked(row) - ) - }) + (row < self.data.len()) + .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)}) + (row < self.data.len()).then(|| unsafe { self.data.get_unchecked(row) }) } /// # Safety @@ -180,7 +191,7 @@ impl Column { 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)}) + (row < self.data.len()).then(|| unsafe { self.data.get_unchecked_mut(row) }) } /// # Safety From e32ec9e8a0f2eae1640e7211ef24e9fadc7428cd Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 03:47:42 -0700 Subject: [PATCH 19/37] Address World review comments --- crates/bevy_ecs/src/world/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index ecb9659fd3b7b..0e0a241868c92 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -683,7 +683,6 @@ impl World { pub unsafe fn remove_resource_unchecked(&mut self) -> Option { let component_id = self.components.get_resource_id(TypeId::of::())?; // SAFE: the resource is of type R and the value is returned back to the caller. - // The access here must not be used on non-send types. unsafe { let (ptr, _) = self.storages.resources.remove(component_id)?; Some(ptr.read::()) @@ -1194,8 +1193,7 @@ impl World { #[inline] unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column { // SAFE: resource archetype always exists - let resources = &mut self.storages.resources; - let resources = &mut resources.resources; + let resources = &mut self.storages.resources.resources; let archetype_component_count = &mut self.archetypes.archetype_component_count; let components = &self.components; &mut resources From 515e2b3ae9c145a3ed1e8185affce5ac1f929f3e Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 05:09:30 -0700 Subject: [PATCH 20/37] Make storage APIs a bit more externally friendly --- crates/bevy_ecs/src/storage/resource.rs | 194 ++++++++++++++---------- crates/bevy_ecs/src/storage/table.rs | 12 ++ crates/bevy_ecs/src/world/mod.rs | 96 +++++------- 3 files changed, 167 insertions(+), 135 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 095631c7094f7..31515871e2067 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,13 +1,99 @@ use crate::archetype::ArchetypeComponentId; use crate::archetype::ArchetypeComponentInfo; -use crate::component::{ComponentId, ComponentTicks}; +use crate::component::{ComponentId, ComponentTicks, Components, StorageType}; use crate::storage::{Column, SparseSet}; use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; use std::cell::UnsafeCell; -pub(crate) struct ResourceInfo { - pub data: Column, - pub component_info: ArchetypeComponentInfo, +pub struct ResourceInfo { + column: Column, + component_info: ArchetypeComponentInfo, +} + +impl ResourceInfo { + /// Returns true if the resource is populated. + #[inline] + pub fn is_present(&self) -> bool { + !self.column.is_empty() + } + + #[inline] + pub(crate) fn component_info(&self) -> &ArchetypeComponentInfo { + &self.component_info + } + + /// 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 mutable pointer to the underlying resource, if available. + #[inline] + pub fn get_data_mut(&mut self) -> Option> { + self.column.get_data_mut(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) + .map(|ticks| unsafe { ticks.deref() }) + } + + /// Gets a mutable reference to the change ticks of the underlying resource, if available. + #[inline] + pub fn get_ticks_mut(&mut self) -> Option<&mut ComponentTicks> { + self.column + .get_ticks(0) + .map(|ticks| unsafe { ticks.deref_mut() }) + } + + #[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. + #[inline] + pub 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)); + } + } + + #[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_mut(0) = change_ticks; + } else { + self.column.push(value, change_ticks); + } + } + + /// Removes a value from the resource, if present. + #[inline] + #[must_use = "The returned pointer to the removed component should be used or dropped"] + pub fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> { + self.column.swap_remove_and_forget(0) + } + + #[inline] + pub(crate) fn remove_and_drop(&mut self) { + self.column.clear(); + } } /// The backing store for all [`Resource`]s stored in the [`World`]. @@ -16,21 +102,10 @@ pub(crate) struct ResourceInfo { /// [`World`]: crate::world::World #[derive(Default)] pub struct Resources { - pub(crate) resources: SparseSet, + resources: SparseSet, } impl Resources { - /// Gets the [`ArchetypeComponentId`] for a given resoruce. - #[inline] - pub fn get_archetype_component_id( - &self, - component_id: ComponentId, - ) -> Option { - self.resources - .get(component_id) - .map(|info| info.component_info.archetype_component_id) - } - /// The total number of resoruces stored in the [`World`] /// /// [`World`]: crate::world::World @@ -50,85 +125,52 @@ impl Resources { /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] - pub fn get(&self, component_id: ComponentId) -> Option> { - self.resources.get(component_id)?.data.get_data(0) + pub fn get(&self, component_id: ComponentId) -> Option<&ResourceInfo> { + self.resources.get(component_id) } /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] - pub fn get_mut(&mut self, component_id: ComponentId) -> Option> { - self.resources.get_mut(component_id)?.data.get_data_mut(0) - } - - /// Gets the [`ComponentTicks`] to a resource, if available. - #[inline] - pub fn get_ticks(&self, component_id: ComponentId) -> Option<&ComponentTicks> { - // SAFE: If row 0 exists, it's ComponentTicks must be valid as well. - self.resources - .get(component_id)? - .data - .get_ticks(0) - .map(|ticks| unsafe { ticks.deref() }) + pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceInfo> { + self.resources.get_mut(component_id) } /// Checks if the a resource is currently stored with a given ID. #[inline] pub fn contains(&self, component_id: ComponentId) -> bool { - self.resources - .get(component_id) - .map(|info| !info.data.is_empty()) + self.get(component_id) + .map(|info| info.is_present()) .unwrap_or(false) } - #[inline] - pub(crate) fn get_with_ticks( - &self, - component_id: ComponentId, - ) -> Option<(Ptr<'_>, &UnsafeCell)> { - self.resources.get(component_id)?.data.get(0) - } - - /// Inserts a resource into the world. + /// Fetches or initializes a new resource and returns back it's underlying column. /// - /// # Safety - /// `data` must point to valid data of this column's component type which - /// must correspond to the provided ID. - #[inline] - pub unsafe fn insert( + /// # Panics + /// Will panic if `component_id` is not valid for the provided `components` + pub fn initialize_with( &mut self, component_id: ComponentId, - data: OwningPtr<'_>, - ticks: ComponentTicks, - ) -> Option<()> { - let column = &mut self.resources.get_mut(component_id)?.data; - debug_assert!(column.is_empty()); - column.push(data, ticks); - Some(()) - } - - /// Removes a resource from the world. - /// - /// # Safety - /// ptr must point to valid data of this column's component type which - /// must correspond to the provided ID. - #[inline] - #[must_use = "The returned pointer to the removed component should be used or dropped"] - pub fn remove(&mut self, component_id: ComponentId) -> Option<(OwningPtr<'_>, ComponentTicks)> { - self.resources - .get_mut(component_id)? - .data - .swap_remove_and_forget(0) - } - - #[inline] - pub(crate) fn remove_and_drop(&mut self, component_id: ComponentId) -> Option<()> { - let column = &mut self.resources.get_mut(component_id)?.data; - (column.len() > 0).then(|| column.clear()) + components: &Components, + f: F, + ) -> &mut ResourceInfo + where + F: FnOnce() -> ArchetypeComponentId, + { + self.resources.get_or_insert_with(component_id, || { + let component_info = components.get_info(component_id).unwrap(); + ResourceInfo { + column: Column::with_capacity(component_info, 1), + component_info: ArchetypeComponentInfo { + archetype_component_id: f(), + storage_type: StorageType::Table, + }, + } + }) } pub fn check_change_ticks(&mut self, change_tick: u32) { for info in self.resources.values_mut() { - info.data.check_change_ticks(change_tick); + 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 965874e4e6df1..480feb038e182 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -73,6 +73,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); + } + /// # Safety /// Assumes data has already been allocated for the given row. #[inline] diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 0e0a241868c92..d557473579cb2 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -8,15 +8,13 @@ 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, ComponentTicks, Components, StorageType, - }, + component::{Component, ComponentDescriptor, ComponentId, ComponentTicks, Components}, entity::{AllocAtWithoutReplacement, Entities, Entity}, query::{QueryState, WorldQuery}, - storage::{Column, ResourceInfo, SparseSet, Storages}, + storage::{ResourceInfo, SparseSet, Storages}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; @@ -684,7 +682,7 @@ impl World { let component_id = self.components.get_resource_id(TypeId::of::())?; // SAFE: the resource is of type R and the value is returned back to the caller. unsafe { - let (ptr, _) = self.storages.resources.remove(component_id)?; + let (ptr, _) = self.storages.resources.get_mut(component_id)?.remove()?; Some(ptr.read::()) } } @@ -701,7 +699,7 @@ impl World { pub fn is_resource_added(&self) -> bool { self.components .get_resource_id(TypeId::of::()) - .and_then(|component_id| self.storages.resources.get_ticks(component_id)) + .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) } @@ -709,7 +707,7 @@ impl World { pub fn is_resource_changed(&self) -> bool { self.components .get_resource_id(TypeId::of::()) - .and_then(|component_id| self.storages.resources.get_ticks(component_id)) + .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) } @@ -878,7 +876,7 @@ impl World { &self, component_id: ComponentId, ) -> Option<(Ptr<'_>, &UnsafeCell)> { - self.storages.resources.get_with_ticks(component_id) + self.storages.resources.get(component_id)?.get_with_ticks() } // Shorthand helper function for getting the [`ArchetypeComponentId`] for a resource. @@ -887,9 +885,8 @@ impl World { &self, component_id: ComponentId, ) -> Option { - self.storages - .resources - .get_archetype_component_id(component_id) + let resource = self.storages.resources.get(component_id)?; + Some(resource.component_info().archetype_component_id) } /// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given @@ -1049,7 +1046,8 @@ impl World { let (ptr, mut ticks) = self .storages .resources - .remove(component_id) + .get_mut(component_id) + .and_then(|info| info.remove()) .unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::())); // SAFE: pointer is of type R // Read the value onto the stack to avoid potential mut aliasing. @@ -1070,7 +1068,8 @@ impl World { // SAFE: pointer is of type R self.storages .resources - .insert(component_id, ptr, ticks) + .get_mut(component_id) + .map(|info| info.insert_with_ticks(ptr, ticks)) .unwrap_or_else(|| { panic!("resource does not exist: {}", std::any::type_name::()) }); @@ -1089,7 +1088,8 @@ impl World { ) -> Option<&R> { self.storages .resources - .get(component_id) + .get(component_id)? + .get_data() .map(|ptr| ptr.deref()) } @@ -1140,17 +1140,11 @@ impl World { #[inline] unsafe fn insert_resource_with_id(&mut self, component_id: ComponentId, value: R) { let change_tick = self.change_tick(); - let column = self.initialize_resource_internal(component_id); - if column.is_empty() { - // SAFE: column is of type R and has been allocated above - OwningPtr::make(value, |ptr| { - column.push(ptr, ComponentTicks::new(change_tick)); - }); - } else { - // SAFE: column is of type R and has already been allocated - *column.get_data_unchecked_mut(0).deref_mut::() = value; - column.get_ticks_unchecked_mut(0).set_changed(change_tick); - } + // SAFE: column is of type R and has been allocated above + OwningPtr::make(value, |ptr| { + self.initialize_resource_internal(component_id) + .insert(ptr, change_tick); + }); } /// Inserts a new resource with the given `value`. Will replace the value if it already existed. @@ -1173,45 +1167,25 @@ impl World { ) }); // SAFE: component_id is valid, checked by the lines above - let column = self.initialize_resource_internal(component_id); - if column.is_empty() { - // SAFE: column is of type R and has been allocated above - column.push(value, ComponentTicks::new(change_tick)); - } else { - let ptr = column.get_data_unchecked_mut(0); - std::ptr::copy_nonoverlapping::( - value.as_ptr(), - ptr.as_ptr(), - column.data.layout().size(), - ); - column.get_ticks_unchecked_mut(0).set_changed(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 { - // SAFE: resource archetype always exists - let resources = &mut self.storages.resources.resources; + unsafe fn initialize_resource_internal( + &mut self, + component_id: ComponentId, + ) -> &mut ResourceInfo { let archetype_component_count = &mut self.archetypes.archetype_component_count; - let components = &self.components; - &mut resources - .get_or_insert_with(component_id, || { - let component_info = components.get_info_unchecked(component_id); - let info = ResourceInfo { - data: Column::with_capacity(component_info, 1), - component_info: 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; - info + id }) - .data } pub(crate) fn initialize_resource(&mut self) -> ComponentId { @@ -1311,7 +1285,7 @@ impl World { if !info.is_send_and_sync() { self.validate_non_send_access_untyped(info.name()); } - self.storages.resources.get(component_id) + self.storages.resources.get(component_id)?.get_data() } /// Gets a resource to the resource with the id [`ComponentId`] if it exists. @@ -1352,7 +1326,11 @@ impl World { if !info.is_send_and_sync() { self.validate_non_send_access_untyped(info.name()); } - self.storages.resources.remove_and_drop(component_id) + self.storages + .resources + .get_mut(component_id)? + .remove_and_drop(); + Some(()) } /// Retrieves a mutable untyped reference to the given `entity`'s [Component] of the given [`ComponentId`]. From c1f1133453e1f817c2cde84e84856f744ba8711e Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Jun 2022 05:17:35 -0700 Subject: [PATCH 21/37] Remove contains --- crates/bevy_ecs/src/storage/resource.rs | 8 -------- crates/bevy_ecs/src/world/mod.rs | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 31515871e2067..c56a6f3a45835 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -135,14 +135,6 @@ impl Resources { self.resources.get_mut(component_id) } - /// Checks if the a resource is currently stored with a given ID. - #[inline] - pub fn contains(&self, component_id: ComponentId) -> bool { - self.get(component_id) - .map(|info| info.is_present()) - .unwrap_or(false) - } - /// Fetches or initializes a new resource and returns back it's underlying column. /// /// # Panics diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index d557473579cb2..ceae72646e6f5 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -692,7 +692,8 @@ impl World { pub fn contains_resource(&self) -> bool { self.components .get_resource_id(TypeId::of::()) - .map(|component_id| self.storages.resources.contains(component_id)) + .and_then(|component_id| self.storages.resources.get(component_id)) + .map(|info| info.is_present()) .unwrap_or(false) } From f4123b9db9f0c53a4b5173058cde16ce30667c0e Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 2 Jul 2022 16:42:06 -0700 Subject: [PATCH 22/37] Fix test compilation --- crates/bevy_ecs/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 3acbb4bb96350..6c3f189884042 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1016,8 +1016,10 @@ mod tests { let archetype_component_id = world .storages() .resources - .get_archetype_component_id(resource_id) - .unwrap(); + .get(resource_id) + .unwrap() + .component_info() + .archetype_component_id; assert_eq!(*world.resource::(), 123); assert!(world.contains_resource::()); @@ -1083,8 +1085,10 @@ mod tests { let current_archetype_component_id = world .storages() .resources - .get_archetype_component_id(current_resource_id) - .unwrap(); + .get(resource_id) + .unwrap() + .component_info() + .archetype_component_id; assert_eq!( archetype_component_id, current_archetype_component_id, From bb38531187f62d059e10a3d5bb4b8acaa201b797 Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 13 Oct 2022 14:05:54 -0700 Subject: [PATCH 23/37] Fix must_use typo Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/storage/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index a709284a8e5ae..124dca6a85ca2 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -125,7 +125,7 @@ impl Column { } #[inline] - #[must_use = "The returned pointer should be used to dropped the removed component"] + #[must_use = "The returned pointer should be used to drop the removed component"] pub(crate) fn swap_remove_and_forget( &mut self, row: usize, From bb59857cd9b59b987086219df31924c4b323f615 Mon Sep 17 00:00:00 2001 From: James Liu Date: Thu, 13 Oct 2022 14:06:15 -0700 Subject: [PATCH 24/37] Change panic message. Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/world/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 83019a167b4d5..25b9a8afd1728 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1133,7 +1133,7 @@ impl World { .get_mut(component_id) .map(|info| info.insert_with_ticks(ptr, ticks)) .unwrap_or_else(|| { - panic!("resource does not exist: {}", std::any::type_name::()) + panic!("No resource of type {} exists in the World.", std::any::type_name::()) }); } }); From 3556580c59fb5036cb7b0e49280e815168af0402 Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 13 Oct 2022 14:26:43 -0700 Subject: [PATCH 25/37] ResourceInfo -> ResourceData --- crates/bevy_ecs/src/storage/resource.rs | 14 +++++++------- crates/bevy_ecs/src/world/mod.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index c56a6f3a45835..1db2ac0b10a6e 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -5,12 +5,12 @@ use crate::storage::{Column, SparseSet}; use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; use std::cell::UnsafeCell; -pub struct ResourceInfo { +pub struct ResourceData { column: Column, component_info: ArchetypeComponentInfo, } -impl ResourceInfo { +impl ResourceData { /// Returns true if the resource is populated. #[inline] pub fn is_present(&self) -> bool { @@ -102,7 +102,7 @@ impl ResourceInfo { /// [`World`]: crate::world::World #[derive(Default)] pub struct Resources { - resources: SparseSet, + resources: SparseSet, } impl Resources { @@ -125,13 +125,13 @@ impl Resources { /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] - pub fn get(&self, component_id: ComponentId) -> Option<&ResourceInfo> { + pub fn get(&self, component_id: ComponentId) -> Option<&ResourceData> { self.resources.get(component_id) } /// Gets a read-only [`Ptr`] to a resource, if available. #[inline] - pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceInfo> { + pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData> { self.resources.get_mut(component_id) } @@ -144,13 +144,13 @@ impl Resources { component_id: ComponentId, components: &Components, f: F, - ) -> &mut ResourceInfo + ) -> &mut ResourceData where F: FnOnce() -> ArchetypeComponentId, { self.resources.get_or_insert_with(component_id, || { let component_info = components.get_info(component_id).unwrap(); - ResourceInfo { + ResourceData { column: Column::with_capacity(component_info, 1), component_info: ArchetypeComponentInfo { archetype_component_id: f(), diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 25b9a8afd1728..ce958ea5e3707 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -16,7 +16,7 @@ use crate::{ }, entity::{AllocAtWithoutReplacement, Entities, Entity}, query::{QueryState, WorldQuery}, - storage::{ResourceInfo, SparseSet, Storages}, + storage::{ResourceData, SparseSet, Storages}, system::Resource, }; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; @@ -1239,7 +1239,7 @@ impl World { unsafe fn initialize_resource_internal( &mut self, component_id: ComponentId, - ) -> &mut ResourceInfo { + ) -> &mut ResourceData { let archetype_component_count = &mut self.archetypes.archetype_component_count; self.storages .resources From 734584d386fa40c7f5769eede3a9b3b892851f13 Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 13 Oct 2022 14:27:52 -0700 Subject: [PATCH 26/37] Type doc comment for ResourceData --- crates/bevy_ecs/src/storage/resource.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 1db2ac0b10a6e..2a0305c71134f 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -5,6 +5,7 @@ use crate::storage::{Column, SparseSet}; use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; use std::cell::UnsafeCell; +/// The type-erased backing storage and metadata for a single resource within a [`World`]. pub struct ResourceData { column: Column, component_info: ArchetypeComponentInfo, From 58027c4bbe4cf968e83262ac1979613bdafa7669 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 15 Oct 2022 22:19:06 -0700 Subject: [PATCH 27/37] Fix CI --- crates/bevy_ecs/src/storage/resource.rs | 5 +++++ crates/bevy_ecs/src/storage/table.rs | 6 +++--- crates/bevy_ecs/src/world/mod.rs | 6 +++--- crates/bevy_ecs/src/world/world_cell.rs | 5 +---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 21bc70e8f956e..44f178c8c14f6 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -6,6 +6,8 @@ use bevy_ptr::{OwningPtr, Ptr, PtrMut, 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, component_info: ArchetypeComponentInfo, @@ -40,6 +42,7 @@ impl ResourceData { pub fn get_ticks(&self) -> Option<&ComponentTicks> { self.column .get_ticks(0) + // SAFETY: If the first row exists, a valid ticks value has been written. .map(|ticks| unsafe { ticks.deref() }) } @@ -48,6 +51,8 @@ impl ResourceData { pub fn get_ticks_mut(&mut self) -> Option<&mut ComponentTicks> { self.column .get_ticks(0) + // SAFETY: If the first row exists, a valid ticks value has been written. + // This function has exclusvie access to the underlying column. .map(|ticks| unsafe { ticks.deref_mut() }) } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 00fe5931c638b..d826b1c306575 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -122,7 +122,7 @@ impl Column { &mut self, row: usize, ) -> Option<(OwningPtr<'_>, ComponentTicks)> { - (row > self.data.len()).then(|| { + (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(); @@ -198,9 +198,9 @@ impl Column { #[inline] pub fn get(&self, row: usize) -> Option<(Ptr<'_>, &UnsafeCell)> { - // 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()) + // 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)) }) } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 2120048bfc5bc..5ee25dc319580 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -786,7 +786,7 @@ impl World { #[allow(unused_unsafe)] pub unsafe fn remove_resource_unchecked(&mut self) -> Option { let component_id = self.components.get_resource_id(TypeId::of::())?; - // SAFE: the resource is of type R and the value is returned back to the caller. + // 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::()) @@ -1169,7 +1169,7 @@ impl World { .and_then(|info| 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. - // SAFE: pointer is of type R + // SAFETY: pointer is of type R let mut value = unsafe { ptr.read::() }; let value_mut = Mut { value: &mut value, @@ -1448,7 +1448,7 @@ impl World { }; Some(MutUntyped { - // SAFE: This function has exclusive access to the world so nothing aliases `ptr`. + // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`. value: unsafe { ptr.assert_unique() }, ticks, }) diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index 0d4ccbf677a39..71c8216b24939 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -381,10 +381,7 @@ mod tests { } } - let u32_component_id = world - .components - .get_resource_id(TypeId::of::()) - .unwrap(); + 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(); From d3d846aa0ae0b27376c8a64173ecfbb628d5ba8f Mon Sep 17 00:00:00 2001 From: James Liu Date: Sun, 16 Oct 2022 16:11:57 -0700 Subject: [PATCH 28/37] Update get's doc comment. Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 44f178c8c14f6..68355d456c6c4 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -129,7 +129,7 @@ impl Resources { self.resources.is_empty() } - /// Gets a read-only [`Ptr`] to a resource, if available. + /// 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) From 40afd474408172062bd52eaacd8803283214799d Mon Sep 17 00:00:00 2001 From: James Liu Date: Sun, 16 Oct 2022 16:12:12 -0700 Subject: [PATCH 29/37] Update get_mut's doc comment. Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 68355d456c6c4..92592ac5a3d4f 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -135,7 +135,7 @@ impl Resources { self.resources.get(component_id) } - /// Gets a read-only [`Ptr`] to a resource, if available. + /// Gets mutable access to a resource, if it exists. #[inline] pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData> { self.resources.get_mut(component_id) From 7b9fbf0e85d6f33fa9abf05f031fea3a5f580bea Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 17 Oct 2022 09:09:52 -0700 Subject: [PATCH 30/37] Fix docs typo Co-authored-by: Boxy --- crates/bevy_ecs/src/storage/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 92592ac5a3d4f..01b37c1594919 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -112,7 +112,7 @@ pub struct Resources { } impl Resources { - /// The total number of resoruces stored in the [`World`] + /// The total number of resources stored in the [`World`] /// /// [`World`]: crate::world::World #[inline] From a6c897708a223d788ba7ea56dc87f6b3e225aa8b Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 17 Oct 2022 09:11:14 -0700 Subject: [PATCH 31/37] Use an impl param instead of a bound Co-authored-by: Boxy --- crates/bevy_ecs/src/storage/resource.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 01b37c1594919..570ef468f1279 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -149,10 +149,8 @@ impl Resources { &mut self, component_id: ComponentId, components: &Components, - f: F, - ) -> &mut ResourceData - where - F: FnOnce() -> ArchetypeComponentId, + f: impl FnOnce() -> ArchetypeComponentId, + ) -> &mut ResourceData, { self.resources.get_or_insert_with(component_id, || { let component_info = components.get_info(component_id).unwrap(); From 39be822b961c480b40b47c86011afad58ceae57f Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 16:46:06 -0700 Subject: [PATCH 32/37] ArchetypeComponentInfo -> Id --- crates/bevy_ecs/src/storage/resource.rs | 15 ++++++--------- crates/bevy_ecs/src/world/mod.rs | 2 +- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 570ef468f1279..d13a431941dc2 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,6 +1,5 @@ use crate::archetype::ArchetypeComponentId; -use crate::archetype::ArchetypeComponentInfo; -use crate::component::{ComponentId, ComponentTicks, Components, StorageType}; +use crate::component::{ComponentId, ComponentTicks, Components}; use crate::storage::{Column, SparseSet}; use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; use std::cell::UnsafeCell; @@ -10,7 +9,7 @@ use std::cell::UnsafeCell; /// [`World`]: crate::world::World pub struct ResourceData { column: Column, - component_info: ArchetypeComponentInfo, + id: ArchetypeComponentId, } impl ResourceData { @@ -20,9 +19,10 @@ impl ResourceData { !self.column.is_empty() } + /// Gets the [`ArchetypeComponentId`] for the resource. #[inline] - pub(crate) fn component_info(&self) -> &ArchetypeComponentInfo { - &self.component_info + pub fn id(&self) -> ArchetypeComponentId { + self.id } /// Gets a read-only pointer to the underlying resource, if available. @@ -156,10 +156,7 @@ impl Resources { let component_info = components.get_info(component_id).unwrap(); ResourceData { column: Column::with_capacity(component_info, 1), - component_info: ArchetypeComponentInfo { - archetype_component_id: f(), - storage_type: StorageType::Table, - }, + id: f(), } }) } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 5ee25dc319580..e4778b3ca51ae 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -993,7 +993,7 @@ impl World { component_id: ComponentId, ) -> Option { let resource = self.storages.resources.get(component_id)?; - Some(resource.component_info().archetype_component_id) + Some(resource.id()) } /// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given From efd51137dd89596d9eb2c0f2c856e20118e89641 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 19:14:11 -0700 Subject: [PATCH 33/37] Remove public access to data methods --- crates/bevy_ecs/src/storage/resource.rs | 31 ++++++------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index d13a431941dc2..a4da1c8419458 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,7 +1,7 @@ use crate::archetype::ArchetypeComponentId; use crate::component::{ComponentId, ComponentTicks, Components}; use crate::storage::{Column, SparseSet}; -use bevy_ptr::{OwningPtr, Ptr, PtrMut, UnsafeCellDeref}; +use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; use std::cell::UnsafeCell; /// The type-erased backing storage and metadata for a single resource within a [`World`]. @@ -31,12 +31,6 @@ impl ResourceData { self.column.get_data(0) } - /// Gets a mutable pointer to the underlying resource, if available. - #[inline] - pub fn get_data_mut(&mut self) -> Option> { - self.column.get_data_mut(0) - } - /// Gets a read-only reference to the change ticks of the underlying resource, if available. #[inline] pub fn get_ticks(&self) -> Option<&ComponentTicks> { @@ -46,16 +40,6 @@ impl ResourceData { .map(|ticks| unsafe { ticks.deref() }) } - /// Gets a mutable reference to the change ticks of the underlying resource, if available. - #[inline] - pub fn get_ticks_mut(&mut self) -> Option<&mut ComponentTicks> { - self.column - .get_ticks(0) - // SAFETY: If the first row exists, a valid ticks value has been written. - // This function has exclusvie access to the underlying column. - .map(|ticks| unsafe { ticks.deref_mut() }) - } - #[inline] pub(crate) fn get_with_ticks(&self) -> Option<(Ptr<'_>, &UnsafeCell)> { self.column.get(0) @@ -67,7 +51,7 @@ impl ResourceData { /// # Safety /// `value` must be valid for the underlying type for the resource. #[inline] - pub unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: u32) { + pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: u32) { if self.is_present() { self.column.replace(0, value, change_tick); } else { @@ -92,7 +76,7 @@ impl ResourceData { /// Removes a value from the resource, if present. #[inline] #[must_use = "The returned pointer to the removed component should be used or dropped"] - pub fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> { + pub(crate) fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> { self.column.swap_remove_and_forget(0) } @@ -137,7 +121,7 @@ impl Resources { /// Gets mutable access to a resource, if it exists. #[inline] - pub fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData> { + pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ResourceData> { self.resources.get_mut(component_id) } @@ -145,13 +129,12 @@ impl Resources { /// /// # Panics /// Will panic if `component_id` is not valid for the provided `components` - pub fn initialize_with( + pub(crate) fn initialize_with( &mut self, component_id: ComponentId, components: &Components, f: impl FnOnce() -> ArchetypeComponentId, - ) -> &mut ResourceData, - { + ) -> &mut ResourceData { self.resources.get_or_insert_with(component_id, || { let component_info = components.get_info(component_id).unwrap(); ResourceData { @@ -161,7 +144,7 @@ impl Resources { }) } - pub fn check_change_ticks(&mut self, change_tick: u32) { + 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); } From 535bc9f565898f41735877027e871152fdb7a112 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 19:25:44 -0700 Subject: [PATCH 34/37] Update safety comments on mutative resource APIs --- crates/bevy_ecs/src/storage/resource.rs | 26 +++++++++++++++++++++++-- crates/bevy_ecs/src/world/mod.rs | 14 ++++++++----- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index a4da1c8419458..8f11a772dc27e 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -50,6 +50,9 @@ impl ResourceData { /// /// # 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`]. #[inline] pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: u32) { if self.is_present() { @@ -59,6 +62,14 @@ impl ResourceData { } } + /// 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`]. #[inline] pub(crate) unsafe fn insert_with_ticks( &mut self, @@ -74,14 +85,25 @@ impl ResourceData { } /// 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. #[inline] #[must_use = "The returned pointer to the removed component should be used or dropped"] - pub(crate) fn remove(&mut self) -> Option<(OwningPtr<'_>, ComponentTicks)> { + 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`]. #[inline] - pub(crate) fn remove_and_drop(&mut self) { + pub(crate) unsafe fn remove_and_drop(&mut self) { self.column.clear(); } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index e4778b3ca51ae..6ae591d355001 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1166,7 +1166,8 @@ impl World { .storages .resources .get_mut(component_id) - .and_then(|info| info.remove()) + // 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 @@ -1463,10 +1464,13 @@ impl World { if !info.is_send_and_sync() { self.validate_non_send_access_untyped(info.name()); } - self.storages - .resources - .get_mut(component_id)? - .remove_and_drop(); + // 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(); + } Some(()) } From 7932fa8d4d013f45ff69173ec2340236267f0895 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 19:49:24 -0700 Subject: [PATCH 35/37] Fix CI --- crates/bevy_ecs/src/lib.rs | 17 +++-------------- crates/bevy_ecs/src/storage/resource.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index ac23368390e8e..8dd9a7bf5e1c7 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -956,13 +956,7 @@ mod tests { .components() .get_resource_id(TypeId::of::()) .unwrap(); - let archetype_component_id = world - .storages() - .resources - .get(resource_id) - .unwrap() - .component_info() - .archetype_component_id; + let archetype_component_id = world.storages().resources.get(resource_id).unwrap().id(); assert_eq!(world.resource::().0, 123); assert!(world.contains_resource::()); @@ -1025,13 +1019,8 @@ mod tests { "resource id does not change after removing / re-adding" ); - let current_archetype_component_id = world - .storages() - .resources - .get(resource_id) - .unwrap() - .component_info() - .archetype_component_id; + 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/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 8f11a772dc27e..4fdad1dbfbba3 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -53,6 +53,8 @@ impl ResourceData { /// /// 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() { @@ -70,6 +72,8 @@ impl ResourceData { /// /// 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, @@ -91,6 +95,8 @@ impl ResourceData { /// 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)> { @@ -102,6 +108,8 @@ impl ResourceData { /// # 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(); From c2476ed9976654a9d1957e617b11aad561655b00 Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 19:53:34 -0700 Subject: [PATCH 36/37] Update safety docs on get_ticks --- crates/bevy_ecs/src/storage/resource.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 4fdad1dbfbba3..f981eb188e10e 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -36,7 +36,10 @@ impl ResourceData { pub fn get_ticks(&self) -> Option<&ComponentTicks> { self.column .get_ticks(0) - // SAFETY: If the first row exists, a valid ticks value has been written. + // 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() }) } From 31cd2b401c52ac8b4e2cf012691a3b63d84fd1ed Mon Sep 17 00:00:00 2001 From: james7132 Date: Mon, 17 Oct 2022 20:08:24 -0700 Subject: [PATCH 37/37] Formatting --- crates/bevy_ecs/src/storage/resource.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index f981eb188e10e..edfa51e12857a 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -36,9 +36,9 @@ impl ResourceData { pub fn get_ticks(&self) -> Option<&ComponentTicks> { self.column .get_ticks(0) - // SAFETY: + // 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 + // - A read-only borrow on self can only exist while a mutable borrow doesn't // exist. .map(|ticks| unsafe { ticks.deref() }) }