diff --git a/benches/benches/bevy_ecs/empty_archetypes.rs b/benches/benches/bevy_ecs/empty_archetypes.rs index 4938120ae9a12..dc40bca48f501 100644 --- a/benches/benches/bevy_ecs/empty_archetypes.rs +++ b/benches/benches/bevy_ecs/empty_archetypes.rs @@ -166,7 +166,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(iter); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); @@ -197,7 +196,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(for_each); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); @@ -228,7 +226,6 @@ fn empty_archetypes(criterion: &mut Criterion) { schedule.add_systems(par_for_each); }); add_archetypes(&mut world, archetype_count); - world.clear_entities(); let mut e = world.spawn_empty(); e.insert(A::<0>(1.0)); e.insert(A::<1>(1.0)); diff --git a/benches/benches/bevy_ecs/main.rs b/benches/benches/bevy_ecs/main.rs index 9e59d31aca00c..bce529de8f5ea 100644 --- a/benches/benches/bevy_ecs/main.rs +++ b/benches/benches/bevy_ecs/main.rs @@ -15,6 +15,7 @@ mod fragmentation; mod iteration; mod observers; mod param; +mod resources; mod scheduling; mod world; @@ -22,6 +23,7 @@ criterion_main!( bundles::benches, change_detection::benches, components::benches, + resources::benches, empty_archetypes::benches, entity_cloning::benches, events::benches, diff --git a/benches/benches/bevy_ecs/resources.rs b/benches/benches/bevy_ecs/resources.rs new file mode 100644 index 0000000000000..736e5daaeeb50 --- /dev/null +++ b/benches/benches/bevy_ecs/resources.rs @@ -0,0 +1,69 @@ +use std::{alloc::Layout, hint::black_box, ptr::NonNull}; + +use benches::bench; +use bevy_ecs::{ + change_detection::MaybeLocation, + component::{ComponentCloneBehavior, ComponentDescriptor, StorageType}, + prelude::*, + ptr::OwningPtr, +}; +use criterion::{criterion_group, Criterion}; + +criterion_group!(benches, get, get_mut, insert_remove); + +fn create_world() -> World { + let mut world = World::new(); + for _ in 0..500 { + // SAFETY: Uses zero-sized value, never drops + unsafe { + let resource_id = + world.register_component_with_descriptor(ComponentDescriptor::new_with_layout( + "", + StorageType::SparseSet, + Layout::new::<()>(), + None, + true, + ComponentCloneBehavior::Default, + None, + )); + world.insert_resource_by_id( + resource_id, + OwningPtr::new(NonNull::dangling()), + MaybeLocation::caller(), + ); + } + } + world +} + +#[derive(Resource)] +struct R; + +pub fn get(criterion: &mut Criterion) { + let mut world = create_world(); + world.insert_resource(R); + criterion.bench_function(bench!("get"), |bencher| { + bencher.iter(|| world.get_resource::()); + }); +} + +pub fn get_mut(criterion: &mut Criterion) { + let mut world = create_world(); + world.insert_resource(R); + criterion.bench_function(bench!("get_mut"), |bencher| { + bencher.iter(|| { + black_box(world.get_resource_mut::()); + }); + }); +} + +pub fn insert_remove(criterion: &mut Criterion) { + let mut world = create_world(); + criterion.bench_function(bench!("insert_remove"), |bencher| { + bencher.iter(|| { + world.insert_resource(R); + black_box(&mut world); + world.remove_resource::() + }); + }); +} diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index c37f48e65b519..48a42ce010cc6 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -47,7 +47,7 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { } }); - let storage = storage_path(&bevy_ecs_path, StorageTy::Table); + let storage = storage_path(&bevy_ecs_path, StorageTy::SparseSet); let on_add_path = None; let on_remove_path = None; diff --git a/crates/bevy_ecs/src/component/constants.rs b/crates/bevy_ecs/src/component/constants.rs index 3f582545f85b9..69f37f6db9020 100644 --- a/crates/bevy_ecs/src/component/constants.rs +++ b/crates/bevy_ecs/src/component/constants.rs @@ -1,14 +1,14 @@ //! Constant components included in every world. -/// `usize` for the [`Add`](crate::lifecycle::Add) component used in lifecycle observers. -pub const ADD: usize = 0; -/// `usize` for the [`Insert`](crate::lifecycle::Insert) component used in lifecycle observers. -pub const INSERT: usize = 1; -/// `usize` for the [`Discard`](crate::lifecycle::Discard) component used in lifecycle observers. -pub const DISCARD: usize = 2; -/// `usize` for the [`Remove`](crate::lifecycle::Remove) component used in lifecycle observers. -pub const REMOVE: usize = 3; -/// `usize` for [`Despawn`](crate::lifecycle::Despawn) component used in lifecycle observers. -pub const DESPAWN: usize = 4; -/// `usize` of the [`IsResource`](crate::resource::IsResource) component used to mark entities with resources. -pub const IS_RESOURCE: usize = 5; +/// `u32` for the [`Add`](crate::lifecycle::Add) component used in lifecycle observers. +pub const ADD: u32 = 0; +/// `u32` for the [`Insert`](crate::lifecycle::Insert) component used in lifecycle observers. +pub const INSERT: u32 = 1; +/// `u32` for the [`Discard`](crate::lifecycle::Discard) component used in lifecycle observers. +pub const DISCARD: u32 = 2; +/// `u32` for the [`Remove`](crate::lifecycle::Remove) component used in lifecycle observers. +pub const REMOVE: u32 = 3; +/// `u32` for [`Despawn`](crate::lifecycle::Despawn) component used in lifecycle observers. +pub const DESPAWN: u32 = 4; +/// `u32` of the [`IsResource`](crate::resource::IsResource) component used to mark entities with resources. +pub const IS_RESOURCE: u32 = 5; diff --git a/crates/bevy_ecs/src/component/identifier.rs b/crates/bevy_ecs/src/component/identifier.rs new file mode 100644 index 0000000000000..97dcc92a76cc3 --- /dev/null +++ b/crates/bevy_ecs/src/component/identifier.rs @@ -0,0 +1,378 @@ +use core::ops::{BitAndAssign, BitOrAssign, SubAssign}; + +use crate::{ + entity::{Entity, EntityHashMap, EntityHashSet, EntityIndex}, + storage::{SparseArray, SparseSetIndex}, +}; +use bevy_ecs_macros::MapEntities; +#[cfg(feature = "bevy_reflect")] +use bevy_reflect::Reflect; +use fixedbitset::FixedBitSet; + +/// A value which uniquely identifies the type of a [`Component`] or [`Resource`] within a +/// [`World`](crate::world::World). +/// +/// Each time a new `Component` type is registered within a `World` using +/// e.g. [`World::register_component`](crate::world::World::register_component) or +/// [`World::register_component_with_descriptor`](crate::world::World::register_component_with_descriptor) +/// or a Resource with e.g. [`World::init_resource`](crate::world::World::init_resource), +/// a corresponding `ComponentId` is created to track it. +/// +/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them +/// into two separate but related concepts allows components to exist outside of Rust's type system. +/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional +/// `ComponentId`s may exist in a `World` to track components which cannot be +/// represented as Rust types for scripting or other advanced use-cases. +/// +/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from +/// one `World` to access the metadata of a `Component` in a different `World` is undefined behavior +/// and must not be attempted. +/// +/// Given a type `T` which implements [`Component`] (including [`Resource`]), the `ComponentId` for `T` can be retrieved +/// from a `World` using [`World::component_id()`](crate::world::World::component_id) or via [`Components::component_id()`]. +#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, MapEntities)] +#[cfg_attr( + feature = "bevy_reflect", + derive(Reflect), + reflect(Debug, Hash, PartialEq, Clone) +)] +pub struct ComponentId(pub(super) Entity); + +impl ComponentId { + /// Creates a new [`ComponentId`]. + /// + /// The `index` is a unique value associated with each type of component in a given world. + /// Usually, this value is taken from a counter incremented for each type of component registered with the world. + #[inline] + pub const fn new(index: Entity) -> ComponentId { + ComponentId(index) + } + + /// Creates a new [`ComponentId`]. + /// + /// Panics if the index is `u32::MAX`. + #[inline] + pub const fn from_u32(index: u32) -> ComponentId { + ComponentId(Entity::from_raw_u32(index).unwrap()) + } + + /// Returns the index of the current component. + // TODO: Track down all uses and improve data structures for performance. + #[inline] + pub fn index(self) -> usize { + self.0.index_u32() as usize + } + + /// Returns the inner entity from the `ComponentId` + #[inline] + pub fn id(self) -> Entity { + self.0 + } +} + +// Identical implementation as Entity +impl SparseSetIndex for ComponentId { + #[inline] + fn sparse_set_index(&self) -> usize { + self.0.sparse_set_index() + } + + #[inline] + fn get_sparse_set_index(value: usize) -> Self { + Self(Entity::get_sparse_set_index(value)) + } +} + +#[derive(Debug)] +pub(crate) struct ComponentIdMap { + dense: SparseArray, + sparse: EntityHashMap, +} + +impl Default for ComponentIdMap { + fn default() -> ComponentIdMap { + ComponentIdMap { + dense: SparseArray::new(), + sparse: EntityHashMap::new(), + } + } +} + +impl ComponentIdMap { + pub(crate) fn contains_key(&self, id: &ComponentId) -> bool { + let index = id.id().index(); + if index.index() < 1024 { + self.dense.contains(index) + } else { + self.sparse.contains_key(&id.id()) + } + } + + pub(crate) fn get(&self, id: &ComponentId) -> Option<&V> { + let index = id.id().index(); + if index.index() < 1024 { + self.dense.get(index) + } else { + self.sparse.get(&id.id()) + } + } + + pub(crate) fn get_mut(&mut self, id: &ComponentId) -> Option<&mut V> { + let index = id.id().index(); + if index.index() < 1024 { + self.dense.get_mut(index) + } else { + self.sparse.get_mut(&id.id()) + } + } + + pub(crate) fn values(&self) -> impl Iterator + '_ { + self.dense.values().chain(self.sparse.values()) + } + + pub(crate) fn len(&self) -> usize { + self.dense.len() + self.sparse.len() + } + + /// # Safety + /// + /// This operation is safe if a key does not exist in the map. + pub unsafe fn insert_unique_unchecked(&mut self, id: ComponentId, value: V) { + let index = id.id().index(); + if index.index() < 1024 { + self.dense.insert(index, value); + } else { + // SAFETY: safety contract is ensured by the caller. + unsafe { self.sparse.insert_unique_unchecked(id.id(), value) }; + } + } +} + +/// A set of [`ComponentId`]s. +#[derive(Default, PartialEq, Eq)] +pub struct ComponentIdSet { + dense: FixedBitSet, + sparse: EntityHashSet, +} + +impl ComponentIdSet { + /// Create a new empty `ComponentIdSet`. + #[inline] + pub const fn new() -> Self { + Self { + dense: FixedBitSet::new(), + sparse: EntityHashSet::new(), + } + } + + #[cfg(test)] + pub(crate) fn from_bits(bits: FixedBitSet) -> Self { + Self { + dense: bits, + sparse: EntityHashSet::new(), + } + } + + /// Adds a [`ComponentId`] to the set. + #[inline] + pub fn insert(&mut self, index: ComponentId) { + let entity = index.id(); + if entity.index_u32() < 1024 { + self.dense.grow_and_insert(index.index()); + } else { + self.sparse.insert(entity); + } + } + + /// Removes a [`ComponentId`] from the set. + #[inline] + pub fn remove(&mut self, index: ComponentId) { + let entity = index.id(); + if entity.index_u32() < 1024 { + if index.index() < self.dense.len() { + self.dense.remove(index.index()); + } + } else { + self.sparse.remove(&entity); + } + } + + /// Removes all [`ComponentId`]s from the set. + #[inline] + pub fn clear(&mut self) { + self.dense.clear(); + self.sparse.clear(); + } + + /// Returns `true` if the [`ComponentId`] is in the set. + #[inline] + pub fn contains(&self, index: ComponentId) -> bool { + let entity = index.id(); + if entity.index_u32() < 1024 { + self.dense.contains(index.index()) + } else { + self.sparse.contains(&entity) + } + } + + /// Returns `true` if `self` has no elements in common with `other`. This + /// is equivalent to checking for an empty intersection. + #[inline] + pub fn is_disjoint(&self, other: &ComponentIdSet) -> bool { + self.dense.is_disjoint(&other.dense) && self.sparse.is_disjoint(&other.sparse) + } + + /// Returns `true` if the set is a subset of another, i.e. `other` contains + /// at least all the values in `self`. + #[inline] + pub fn is_subset(&self, other: &ComponentIdSet) -> bool { + self.dense.is_subset(&other.dense) && self.sparse.is_subset(&other.sparse) + } + + /// Returns `true` if the set is empty. + #[inline] + pub fn is_clear(&self) -> bool { + self.dense.is_clear() && self.sparse.is_empty() + } + + /// Iterates the [`ComponentId`]s in the set. + #[inline] + pub fn iter(&self) -> impl Iterator { + self.dense + .ones() + .map(|id| ComponentId::from_u32(id as u32)) + .chain(self.sparse.iter().map(|entity| ComponentId::new(*entity))) + } + + /// Returns a lazy iterator over the union of two [`ComponentIdSet`]s. + #[inline] + pub fn union<'a>(&'a self, other: &'a ComponentIdSet) -> impl Iterator { + self.dense + .union(&other.dense) + .map(|id| ComponentId::from_u32(id as u32)) + .chain( + self.sparse + .union(&other.sparse) + .map(|entity| ComponentId::new(*entity)), + ) + } + + /// Returns a lazy iterator over the intersection of two [`ComponentIdSet`]s. + #[inline] + pub fn intersection<'a>( + &'a self, + other: &'a ComponentIdSet, + ) -> impl Iterator { + self.dense + .intersection(&other.dense) + .map(|id| ComponentId::from_u32(id as u32)) + .chain( + self.sparse + .intersection(&other.sparse) + .map(|entity| ComponentId::new(*entity)), + ) + } + + /// Returns a lazy iterator over the difference of two [`ComponentIdSet`]s. + #[inline] + pub fn difference<'a>( + &'a self, + other: &'a ComponentIdSet, + ) -> impl Iterator { + self.dense + .difference(&other.dense) + .map(|id| ComponentId::from_u32(id as u32)) + .chain( + self.sparse + .difference(&other.sparse) + .map(|entity| ComponentId::new(*entity)), + ) + } + + /// In-place union of two [`ComponentIdSet`]s. + #[inline] + pub fn union_with(&mut self, other: &ComponentIdSet) { + self.dense.union_with(&other.dense); + self.sparse.bitor_assign(&other.sparse); + } + + /// In-place intersection of two [`ComponentIdSet`]s. + #[inline] + pub fn intersect_with(&mut self, other: &ComponentIdSet) { + self.dense.intersect_with(&other.dense); + self.sparse.bitand_assign(&other.sparse); + } + + /// In-place difference of two [`ComponentIdSet`]s. + #[inline] + pub fn difference_with(&mut self, other: &ComponentIdSet) { + self.dense.difference_with(&other.dense); + self.sparse.sub_assign(&other.sparse); + } + + /// In-place reversed difference of two [`ComponentIdSet`]s. + /// This sets `self` to be `other.difference(self)`. + #[inline] + pub fn difference_from(&mut self, other: &ComponentIdSet) { + // Calculate `other - self` as `!self & other` + // We have to grow here because the new bits are going to get flipped to 1. + self.dense.grow(other.dense.len()); + self.dense.toggle_range(..); + self.dense.intersect_with(&other.dense); + self.sparse.clone_from(&(&other.sparse - &self.sparse)); + } +} + +impl Clone for ComponentIdSet { + #[inline] + fn clone(&self) -> Self { + ComponentIdSet { + dense: self.dense.clone(), + sparse: self.sparse.clone(), + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.dense.clone_from(&source.dense); + self.sparse.clone_from(&source.sparse); + } +} + +impl core::fmt::Debug for ComponentIdSet { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + // `FixedBitSet` normally has a `Debug` output like: + // FixedBitSet { data: [ 160 ], length: 8 } + // Instead, print the list of set values, like: + // [ 5, 7 ] + // Don't wrap in `ComponentId`, since that would just output: + // [ ComponentId(5), ComponentId(7) ] + f.debug_list() + .entries( + self.dense + .ones() + .chain(self.sparse.iter().map(|index| index.index_u32() as usize)), + ) + .finish() + } +} + +impl FromIterator for ComponentIdSet { + #[inline] + fn from_iter>(iter: T) -> Self { + let mut set = ComponentIdSet::new(); + for index in iter { + set.insert(index); + } + set + } +} + +impl Extend for ComponentIdSet { + #[inline] + fn extend>(&mut self, iter: T) { + for index in iter { + self.insert(index); + } + } +} diff --git a/crates/bevy_ecs/src/component/info.rs b/crates/bevy_ecs/src/component/info.rs index 567cbb7bb77c4..ac4b4785eacfc 100644 --- a/crates/bevy_ecs/src/component/info.rs +++ b/crates/bevy_ecs/src/component/info.rs @@ -1,8 +1,6 @@ -use alloc::{borrow::Cow, vec::Vec}; +use alloc::borrow::Cow; use bevy_platform::{hash::FixedHasher, sync::PoisonError}; use bevy_ptr::OwningPtr; -#[cfg(feature = "bevy_reflect")] -use bevy_reflect::Reflect; use bevy_utils::{prelude::DebugName, TypeIdMap}; use core::{ alloc::Layout, @@ -15,8 +13,8 @@ use indexmap::IndexSet; use crate::{ archetype::ArchetypeFlags, component::{ - Component, ComponentCloneBehavior, ComponentMutability, QueuedComponents, - RequiredComponents, StorageType, + Component, ComponentCloneBehavior, ComponentId, ComponentIdMap, ComponentMutability, + QueuedComponents, RequiredComponents, StorageType, }, lifecycle::ComponentHooks, query::DebugCheckedUnwrap as _, @@ -24,7 +22,6 @@ use crate::{ MaybeRelationshipAccessor, RelationshipAccessor, RelationshipAccessorInitializer, }, resource::Resource, - storage::SparseSetIndex, }; /// Stores metadata for a type of component or resource stored in a specific [`World`](crate::world::World). @@ -151,64 +148,6 @@ impl ComponentInfo { } } -/// A value which uniquely identifies the type of a [`Component`] or [`Resource`] within a -/// [`World`](crate::world::World). -/// -/// Each time a new `Component` type is registered within a `World` using -/// e.g. [`World::register_component`](crate::world::World::register_component) or -/// [`World::register_component_with_descriptor`](crate::world::World::register_component_with_descriptor) -/// or a Resource with e.g. [`World::init_resource`](crate::world::World::init_resource), -/// a corresponding `ComponentId` is created to track it. -/// -/// While the distinction between `ComponentId` and [`TypeId`] may seem superficial, breaking them -/// into two separate but related concepts allows components to exist outside of Rust's type system. -/// Each Rust type registered as a `Component` will have a corresponding `ComponentId`, but additional -/// `ComponentId`s may exist in a `World` to track components which cannot be -/// represented as Rust types for scripting or other advanced use-cases. -/// -/// A `ComponentId` is tightly coupled to its parent `World`. Attempting to use a `ComponentId` from -/// one `World` to access the metadata of a `Component` in a different `World` is undefined behavior -/// and must not be attempted. -/// -/// Given a type `T` which implements [`Component`] (including [`Resource`]), the `ComponentId` for `T` can be retrieved -/// from a `World` using [`World::component_id()`](crate::world::World::component_id) or via [`Components::component_id()`]. -#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr( - feature = "bevy_reflect", - derive(Reflect), - reflect(Debug, Hash, PartialEq, Clone) -)] -pub struct ComponentId(pub(super) usize); - -impl ComponentId { - /// Creates a new [`ComponentId`]. - /// - /// The `index` is a unique value associated with each type of component in a given world. - /// Usually, this value is taken from a counter incremented for each type of component registered with the world. - #[inline] - pub const fn new(index: usize) -> ComponentId { - ComponentId(index) - } - - /// Returns the index of the current component. - #[inline] - pub fn index(self) -> usize { - self.0 - } -} - -impl SparseSetIndex for ComponentId { - #[inline] - fn sparse_set_index(&self) -> usize { - self.index() - } - - #[inline] - fn get_sparse_set_index(value: usize) -> Self { - Self(value) - } -} - /// A value describing a component or resource, which may or may not correspond to a Rust type. #[derive(Clone)] pub struct ComponentDescriptor { @@ -355,7 +294,7 @@ impl ComponentDescriptor { /// Stores metadata associated with each kind of [`Component`] in a given [`World`](crate::world::World). #[derive(Debug, Default)] pub struct Components { - pub(super) components: Vec>, + pub(super) components: ComponentIdMap, pub(super) indices: TypeIdMap, // This is kept internal and local to verify that no deadlocks can occur. pub(super) queued: bevy_platform::sync::RwLock, @@ -375,15 +314,11 @@ impl Components { ) { descriptor.initialize(id, self); let info = ComponentInfo::new(id, descriptor); - let least_len = id.0 + 1; - if self.components.len() < least_len { - self.components.resize_with(least_len, || None); + debug_assert!(!self.components.contains_key(&id)); + // SAFETY: The id has never been registered before. + unsafe { + self.components.insert_unique_unchecked(id, info); } - // SAFETY: We just extended the vec to make this index valid. - let slot = unsafe { self.components.get_mut(id.0).debug_checked_unwrap() }; - // Caller ensures id is unique - debug_assert!(slot.is_none()); - *slot = Some(info); } /// Returns the number of components registered or queued with this instance. @@ -445,7 +380,7 @@ impl Components { /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. #[inline] pub fn get_info(&self, id: ComponentId) -> Option<&ComponentInfo> { - self.components.get(id.0).and_then(|info| info.as_ref()) + self.components.get(&id) } /// Gets the [`ComponentDescriptor`] of the component with this [`ComponentId`] if it is present. @@ -457,8 +392,8 @@ impl Components { #[inline] pub fn get_descriptor<'a>(&'a self, id: ComponentId) -> Option> { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| Cow::Borrowed(&info.descriptor))) + .get(&id) + .map(|info| Cow::Borrowed(&info.descriptor)) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -478,8 +413,8 @@ impl Components { #[inline] pub fn get_name<'a>(&'a self, id: ComponentId) -> Option { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| info.descriptor.name())) + .get(&id) + .map(|info| info.descriptor.name()) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -499,27 +434,19 @@ impl Components { #[inline] pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo { // SAFETY: The caller ensures `id` is valid. - unsafe { - self.components - .get(id.0) - .debug_checked_unwrap() - .as_ref() - .debug_checked_unwrap() - } + unsafe { self.components.get(&id).debug_checked_unwrap() } } #[inline] pub(crate) fn get_hooks_mut(&mut self, id: ComponentId) -> Option<&mut ComponentHooks> { - self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.hooks)) + self.components.get_mut(&id).map(|info| &mut info.hooks) } #[inline] pub(crate) fn get_required_components(&self, id: ComponentId) -> Option<&RequiredComponents> { self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| &info.required_components)) + .get(&id) + .map(|info| &info.required_components) } #[inline] @@ -528,8 +455,8 @@ impl Components { id: ComponentId, ) -> Option<&mut RequiredComponents> { self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.required_components)) + .get_mut(&id) + .map(|info| &mut info.required_components) } #[inline] @@ -537,9 +464,7 @@ impl Components { &self, id: ComponentId, ) -> Option<&IndexSet> { - self.components - .get(id.0) - .and_then(|info| info.as_ref().map(|info| &info.required_by)) + self.components.get(&id).map(|info| &info.required_by) } #[inline] @@ -548,8 +473,8 @@ impl Components { id: ComponentId, ) -> Option<&mut IndexSet> { self.components - .get_mut(id.0) - .and_then(|info| info.as_mut().map(|info| &mut info.required_by)) + .get_mut(&id) + .map(|info| &mut info.required_by) } /// Returns true if the [`ComponentId`] is fully registered and valid. @@ -557,7 +482,7 @@ impl Components { /// Those ids are still correct, but they are not usable in every context yet. #[inline] pub fn is_id_valid(&self, id: ComponentId) -> bool { - self.components.get(id.0).is_some_and(Option::is_some) + self.components.get(&id).is_some() } /// Type-erased equivalent of [`Components::valid_component_id()`]. @@ -739,7 +664,7 @@ impl Components { /// Gets an iterator over all components fully registered with this instance. pub fn iter_registered(&self) -> impl Iterator + '_ { - self.components.iter().filter_map(Option::as_ref) + self.components.values() } pub(crate) fn get_relationship_accessor_mut( @@ -747,10 +672,7 @@ impl Components { component_id: ComponentId, ) -> Option<&mut MaybeRelationshipAccessor> { self.components - .get_mut(component_id.index()) - .and_then(|info| { - info.as_mut() - .map(|info| &mut info.descriptor.relationship_accessor) - }) + .get_mut(&component_id) + .map(|info| &mut info.descriptor.relationship_accessor) } } diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index a30f7379787ae..6ef5fb660ffae 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -2,12 +2,14 @@ mod clone; mod constants; +mod identifier; mod info; mod register; mod required; pub use clone::*; pub use constants::*; +pub use identifier::*; pub use info::*; pub use register::*; pub use required::*; diff --git a/crates/bevy_ecs/src/component/register.rs b/crates/bevy_ecs/src/component/register.rs index 10e4e137a9f06..4339ac701c93b 100644 --- a/crates/bevy_ecs/src/component/register.rs +++ b/crates/bevy_ecs/src/component/register.rs @@ -5,6 +5,7 @@ use core::any::Any; use core::{any::TypeId, fmt::Debug, ops::Deref}; use crate::component::{enforce_no_required_components_recursion, RequiredComponentsRegistrator}; +use crate::entity::EntityAllocator; use crate::lifecycle::ComponentHooks; use crate::{ component::{ @@ -14,57 +15,10 @@ use crate::{ resource::Resource, }; -/// Generates [`ComponentId`]s. -#[derive(Debug, Default)] -pub struct ComponentIds { - next: bevy_platform::sync::atomic::AtomicUsize, -} - -impl ComponentIds { - /// Peeks the next [`ComponentId`] to be generated without generating it. - pub fn peek(&self) -> ComponentId { - ComponentId( - self.next - .load(bevy_platform::sync::atomic::Ordering::Relaxed), - ) - } - - /// Generates and returns the next [`ComponentId`]. - pub fn next(&self) -> ComponentId { - ComponentId( - self.next - .fetch_add(1, bevy_platform::sync::atomic::Ordering::Relaxed), - ) - } - - /// Peeks the next [`ComponentId`] to be generated without generating it. - pub fn peek_mut(&mut self) -> ComponentId { - ComponentId(*self.next.get_mut()) - } - - /// Generates and returns the next [`ComponentId`]. - pub fn next_mut(&mut self) -> ComponentId { - let id = self.next.get_mut(); - let result = ComponentId(*id); - *id += 1; - result - } - - /// Returns the number of [`ComponentId`]s generated. - pub fn len(&self) -> usize { - self.peek().0 - } - - /// Returns true if and only if no ids have been generated. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - /// A [`Components`] wrapper that enables additional features, like registration. pub struct ComponentsRegistrator<'w> { pub(super) components: &'w mut Components, - pub(super) ids: &'w mut ComponentIds, + pub(super) ids: &'w mut EntityAllocator, pub(super) recursion_check_stack: Vec, } @@ -81,9 +35,9 @@ impl<'w> ComponentsRegistrator<'w> { /// /// # Safety /// - /// The [`Components`] and [`ComponentIds`] must match. + /// The [`Components`] and [`ComponentId`]s must match. /// For example, they must be from the same world. - pub unsafe fn new(components: &'w mut Components, ids: &'w mut ComponentIds) -> Self { + pub unsafe fn new(components: &'w mut Components, ids: &'w mut EntityAllocator) -> Self { Self { components, ids, @@ -201,7 +155,7 @@ impl<'w> ComponentsRegistrator<'w> { return registrator.register(self); } - let id = self.ids.next_mut(); + let id = ComponentId::new(self.ids.alloc()); // SAFETY: The component is not currently registered, and the id is fresh. unsafe { self.register_component_unchecked( @@ -255,9 +209,7 @@ impl<'w> ComponentsRegistrator<'w> { &mut self .components .components - .get_mut(id.0) - .debug_checked_unwrap() - .as_mut() + .get_mut(&id) .debug_checked_unwrap() }; @@ -288,7 +240,7 @@ impl<'w> ComponentsRegistrator<'w> { &mut self, descriptor: ComponentDescriptor, ) -> ComponentId { - let id = self.ids.next_mut(); + let id = ComponentId::new(self.ids.alloc()); // SAFETY: The id is fresh. unsafe { self.components.register_component_inner(id, descriptor); @@ -350,7 +302,7 @@ impl<'w> ComponentsRegistrator<'w> { return registrator.register(self); } - let id = self.ids.next_mut(); + let id = ComponentId::new(self.ids.alloc()); // SAFETY: The resource is not currently registered, the id is fresh, and the [`ComponentDescriptor`] matches the [`TypeId`] unsafe { self.components @@ -447,7 +399,7 @@ impl Debug for QueuedComponents { #[derive(Clone, Copy)] pub struct ComponentsQueuedRegistrator<'w> { components: &'w Components, - ids: &'w ComponentIds, + ids: &'w EntityAllocator, } impl Deref for ComponentsQueuedRegistrator<'_> { @@ -463,9 +415,9 @@ impl<'w> ComponentsQueuedRegistrator<'w> { /// /// # Safety /// - /// The [`Components`] and [`ComponentIds`] must match. + /// The [`Components`] and [`ComponentId`]s must match. /// For example, they must be from the same world. - pub unsafe fn new(components: &'w Components, ids: &'w ComponentIds) -> Self { + pub unsafe fn new(components: &'w Components, ids: &'w EntityAllocator) -> Self { Self { components, ids } } @@ -489,7 +441,9 @@ impl<'w> ComponentsQueuedRegistrator<'w> { .entry(type_id) .or_insert_with(|| { // SAFETY: The id was just generated. - unsafe { QueuedRegistration::new(self.ids.next(), descriptor, func) } + unsafe { + QueuedRegistration::new(ComponentId::new(self.ids.alloc()), descriptor, func) + } }) .id } @@ -500,7 +454,7 @@ impl<'w> ComponentsQueuedRegistrator<'w> { descriptor: ComponentDescriptor, func: fn(&mut ComponentsRegistrator, ComponentId, ComponentDescriptor), ) -> ComponentId { - let id = self.ids.next(); + let id = ComponentId::new(self.ids.alloc()); self.components .queued .write() diff --git a/crates/bevy_ecs/src/entity_disabling.rs b/crates/bevy_ecs/src/entity_disabling.rs index 1e751070ac2df..46ff6237ee2ab 100644 --- a/crates/bevy_ecs/src/entity_disabling.rs +++ b/crates/bevy_ecs/src/entity_disabling.rs @@ -253,42 +253,44 @@ mod tests { #[test] fn filters_modify_access() { let mut filters = DefaultQueryFilters::empty(); - filters.register_disabling_component(ComponentId::new(1)); + filters.register_disabling_component(ComponentId::from_u32(1)); // A component access with an unrelated component let mut component_access = FilteredAccess::default(); - component_access.access_mut().add_read(ComponentId::new(2)); + component_access + .access_mut() + .add_read(ComponentId::from_u32(2)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!(0, applied_access.with_filters().count()); assert_eq!( - vec![ComponentId::new(1)], + vec![ComponentId::from_u32(1)], applied_access.without_filters().collect::>() ); // We add a with filter, now we expect to see both filters - component_access.and_with(ComponentId::new(4)); + component_access.and_with(ComponentId::from_u32(4)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(4)], + vec![ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!( - vec![ComponentId::new(1)], + vec![ComponentId::from_u32(1)], applied_access.without_filters().collect::>() ); let copy = component_access.clone(); // We add a rule targeting a default component, that filter should no longer be added - component_access.and_with(ComponentId::new(1)); + component_access.and_with(ComponentId::from_u32(1)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(1), ComponentId::new(4)], + vec![ComponentId::from_u32(1), ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!(0, applied_access.without_filters().count()); @@ -297,12 +299,12 @@ mod tests { component_access = copy.clone(); component_access .access_mut() - .add_archetypal(ComponentId::new(1)); + .add_archetypal(ComponentId::from_u32(1)); let mut applied_access = component_access.clone(); filters.modify_access(&mut applied_access); assert_eq!( - vec![ComponentId::new(4)], + vec![ComponentId::from_u32(4)], applied_access.with_filters().collect::>() ); assert_eq!(0, applied_access.without_filters().count()); diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index 82e73d326c215..cb3638bb4c8d1 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -315,15 +315,15 @@ impl ComponentHooks { } /// [`EventKey`] for [`Add`] -pub const ADD: EventKey = EventKey(ComponentId::new(crate::component::ADD)); +pub const ADD: EventKey = EventKey(ComponentId::from_u32(crate::component::ADD)); /// [`EventKey`] for [`Insert`] -pub const INSERT: EventKey = EventKey(ComponentId::new(crate::component::INSERT)); +pub const INSERT: EventKey = EventKey(ComponentId::from_u32(crate::component::INSERT)); /// [`EventKey`] for [`Discard`] -pub const DISCARD: EventKey = EventKey(ComponentId::new(crate::component::DISCARD)); +pub const DISCARD: EventKey = EventKey(ComponentId::from_u32(crate::component::DISCARD)); /// [`EventKey`] for [`Remove`] -pub const REMOVE: EventKey = EventKey(ComponentId::new(crate::component::REMOVE)); +pub const REMOVE: EventKey = EventKey(ComponentId::from_u32(crate::component::REMOVE)); /// [`EventKey`] for [`Despawn`] -pub const DESPAWN: EventKey = EventKey(ComponentId::new(crate::component::DESPAWN)); +pub const DESPAWN: EventKey = EventKey(ComponentId::from_u32(crate::component::DESPAWN)); /// Trigger emitted when a component is inserted onto an entity that does not already have that /// component. Runs before `Insert`. diff --git a/crates/bevy_ecs/src/name.rs b/crates/bevy_ecs/src/name.rs index 721881542858f..6ac8c72fbe400 100644 --- a/crates/bevy_ecs/src/name.rs +++ b/crates/bevy_ecs/src/name.rs @@ -293,7 +293,7 @@ mod tests { let mut query = world.query::(); let d1 = query.get(&world, e1).unwrap(); // NameOrEntity Display for entities without a Name should be {index}v{generation} - assert_eq!(d1.to_string(), "1v0"); + assert_eq!(d1.to_string(), "9v0"); let d2 = query.get(&world, e2).unwrap(); // NameOrEntity Display for entities with a Name should be the Name assert_eq!(d2.to_string(), "MyName"); diff --git a/crates/bevy_ecs/src/observer/centralized_storage.rs b/crates/bevy_ecs/src/observer/centralized_storage.rs index 8b09e95a0ec90..2c712abdda8f6 100644 --- a/crates/bevy_ecs/src/observer/centralized_storage.rs +++ b/crates/bevy_ecs/src/observer/centralized_storage.rs @@ -39,14 +39,20 @@ impl Observers { pub(crate) fn get_observers_mut(&mut self, event_key: EventKey) -> &mut CachedObservers { use crate::lifecycle::*; - match event_key { - ADD => &mut self.add, - INSERT => &mut self.insert, - DISCARD => &mut self.discard, - REMOVE => &mut self.remove, - DESPAWN => &mut self.despawn, - _ => self.cache.entry(event_key).or_default(), + // We can't use a constant match expression due to Entity having a manual `PartialEq` impl. + // see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html + if event_key == ADD { + return &mut self.add; + } else if event_key == INSERT { + return &mut self.insert; + } else if event_key == DISCARD { + return &mut self.discard; + } else if event_key == REMOVE { + return &mut self.remove; + } else if event_key == DESPAWN { + return &mut self.despawn; } + self.cache.entry(event_key).or_default() } /// Attempts to get the observers for the given `event_key`. @@ -62,27 +68,39 @@ impl Observers { pub fn try_get_observers(&self, event_key: EventKey) -> Option<&CachedObservers> { use crate::lifecycle::*; - match event_key { - ADD => Some(&self.add), - INSERT => Some(&self.insert), - DISCARD => Some(&self.discard), - REMOVE => Some(&self.remove), - DESPAWN => Some(&self.despawn), - _ => self.cache.get(&event_key), + // We can't use a constant match expression due to Entity having a manual `PartialEq` impl. + // see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html + if event_key == ADD { + return Some(&self.add); + } else if event_key == INSERT { + return Some(&self.insert); + } else if event_key == DISCARD { + return Some(&self.discard); + } else if event_key == REMOVE { + return Some(&self.remove); + } else if event_key == DESPAWN { + return Some(&self.despawn); } + self.cache.get(&event_key) } pub(crate) fn is_archetype_cached(event_key: EventKey) -> Option { use crate::lifecycle::*; - match event_key { - ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER), - INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER), - DISCARD => Some(ArchetypeFlags::ON_DISCARD_OBSERVER), - REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER), - DESPAWN => Some(ArchetypeFlags::ON_DESPAWN_OBSERVER), - _ => None, + // We can't use a constant match expression due to Entity having a manual `PartialEq` impl. + // see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html + if event_key == ADD { + return Some(ArchetypeFlags::ON_ADD_OBSERVER); + } else if event_key == INSERT { + return Some(ArchetypeFlags::ON_INSERT_OBSERVER); + } else if event_key == DISCARD { + return Some(ArchetypeFlags::ON_DISCARD_OBSERVER); + } else if event_key == REMOVE { + return Some(ArchetypeFlags::ON_REMOVE_OBSERVER); + } else if event_key == DESPAWN { + return Some(ArchetypeFlags::ON_DESPAWN_OBSERVER); } + None } pub(crate) fn update_archetype_flags( diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 2a2189810d074..caf5c38026cf1 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -646,7 +646,7 @@ mod tests { world.add_observer(|_: On, mut res: ResMut| res.observed("add_2")); world.spawn(A).flush(); - assert_eq!(vec!["add_2", "add_1"], world.resource::().0); + assert_eq!(vec!["add_1", "add_2"], world.resource::().0); // we have one A entity and two observers assert_eq!(world.query::<&A>().query(&world).count(), 1); assert_eq!(world.query::<&Observer>().query(&world).count(), 2); diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index f16e318bf7b5f..de84bbf87cc37 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -1,17 +1,18 @@ use crate::world::unsafe_world_cell::UnsafeWorldCell; -use crate::{component::ComponentId, resource::IS_RESOURCE}; +use crate::{ + component::{ComponentId, ComponentIdSet}, + resource::IS_RESOURCE, +}; use alloc::{format, string::String, vec, vec::Vec}; use core::iter::FusedIterator; -use core::{fmt, fmt::Debug}; use derive_more::From; -use fixedbitset::{Difference, FixedBitSet, Intersection, IntoOnes, Ones, Union}; use thiserror::Error; /// Tracks read and write access to specific elements in a collection. /// /// Used internally to ensure soundness during system initialization and execution. /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions. -#[derive(Eq, PartialEq, Default, Hash, Debug)] +#[derive(Eq, PartialEq, Default, Debug)] pub struct Access { /// All accessed components, or forbidden components if /// `Self::component_read_and_writes_inverted` is set. @@ -581,9 +582,9 @@ impl Access { /// # use bevy_ecs::component::ComponentId; /// let mut access = Access::default(); /// - /// access.add_read(ComponentId::new(1)); - /// access.add_write(ComponentId::new(2)); - /// access.add_archetypal(ComponentId::new(3)); + /// access.add_read(ComponentId::from_u32(1)); + /// access.add_write(ComponentId::from_u32(2)); + /// access.add_archetypal(ComponentId::from_u32(3)); /// /// let result = access /// .try_iter_access() @@ -592,9 +593,9 @@ impl Access { /// assert_eq!( /// result, /// Ok(vec![ - /// ComponentAccessKind::Shared(ComponentId::new(1)), - /// ComponentAccessKind::Exclusive(ComponentId::new(2)), - /// ComponentAccessKind::Archetypal(ComponentId::new(3)), + /// ComponentAccessKind::Shared(ComponentId::from_u32(1)), + /// ComponentAccessKind::Exclusive(ComponentId::from_u32(2)), + /// ComponentAccessKind::Archetypal(ComponentId::from_u32(3)), /// ]), /// ); /// ``` @@ -772,7 +773,7 @@ impl AccessConflicts { *s = AccessConflicts::All; } (AccessConflicts::Individual(this), AccessConflicts::Individual(other)) => { - this.extend(other); + this.extend(other.iter()); } _ => {} } @@ -1281,192 +1282,11 @@ impl FilteredAccessSet { } } -/// A set of [`ComponentId`]s. -#[derive(Default, Eq, PartialEq, Hash)] -#[repr(transparent)] -pub struct ComponentIdSet(FixedBitSet); - -impl ComponentIdSet { - /// Create a new empty `ComponentIdSet`. - #[inline] - pub const fn new() -> Self { - Self(FixedBitSet::new()) - } - - #[cfg(test)] - pub(crate) fn from_bits(bits: FixedBitSet) -> Self { - Self(bits) - } - - /// Adds a [`ComponentId`] to the set. - #[inline] - pub fn insert(&mut self, index: ComponentId) { - self.0.grow_and_insert(index.index()); - } - - /// Removes a [`ComponentId`] from the set. - #[inline] - pub fn remove(&mut self, index: ComponentId) { - if index.index() < self.0.len() { - self.0.remove(index.index()); - } - } - - /// Removes all [`ComponentId`]s from the set. - #[inline] - pub fn clear(&mut self) { - self.0.clear(); - } - - /// Returns `true` if the [`ComponentId`] is in the set. - #[inline] - pub fn contains(&self, index: ComponentId) -> bool { - self.0.contains(index.index()) - } - - /// Returns `true` if `self` has no elements in common with `other`. This - /// is equivalent to checking for an empty intersection. - #[inline] - pub fn is_disjoint(&self, other: &ComponentIdSet) -> bool { - self.0.is_disjoint(&other.0) - } - - /// Returns `true` if the set is a subset of another, i.e. `other` contains - /// at least all the values in `self`. - #[inline] - pub fn is_subset(&self, other: &ComponentIdSet) -> bool { - self.0.is_subset(&other.0) - } - - /// Returns `true` if the set is empty. - #[inline] - pub fn is_clear(&self) -> bool { - self.0.is_clear() - } - - /// Iterates the [`ComponentId`]s in the set. - #[inline] - pub fn iter(&self) -> ComponentIdIter> { - ComponentIdIter(self.0.ones()) - } - - /// Returns a lazy iterator over the union of two [`ComponentIdSet`]s. - #[inline] - pub fn union<'a>(&'a self, other: &'a ComponentIdSet) -> ComponentIdIter> { - ComponentIdIter(self.0.union(&other.0)) - } - - /// Returns a lazy iterator over the intersection of two [`ComponentIdSet`]s. - #[inline] - pub fn intersection<'a>( - &'a self, - other: &'a ComponentIdSet, - ) -> ComponentIdIter> { - ComponentIdIter(self.0.intersection(&other.0)) - } - - /// Returns a lazy iterator over the difference of two [`ComponentIdSet`]s. - #[inline] - pub fn difference<'a>(&'a self, other: &'a ComponentIdSet) -> ComponentIdIter> { - ComponentIdIter(self.0.difference(&other.0)) - } - - /// In-place union of two [`ComponentIdSet`]s. - #[inline] - pub fn union_with(&mut self, other: &ComponentIdSet) { - self.0.union_with(&other.0); - } - - /// In-place intersection of two [`ComponentIdSet`]s. - #[inline] - pub fn intersect_with(&mut self, other: &ComponentIdSet) { - self.0.intersect_with(&other.0); - } - - /// In-place difference of two [`ComponentIdSet`]s. - #[inline] - pub fn difference_with(&mut self, other: &ComponentIdSet) { - self.0.difference_with(&other.0); - } - - /// In-place reversed difference of two [`ComponentIdSet`]s. - /// This sets `self` to be `other.difference(self)`. - #[inline] - pub fn difference_from(&mut self, other: &ComponentIdSet) { - // Calculate `other - self` as `!self & other` - // We have to grow here because the new bits are going to get flipped to 1. - self.0.grow(other.0.len()); - self.0.toggle_range(..); - self.0.intersect_with(&other.0); - } -} - -impl Debug for ComponentIdSet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // `FixedBitSet` normally has a `Debug` output like: - // FixedBitSet { data: [ 160 ], length: 8 } - // Instead, print the list of set values, like: - // [ 5, 7 ] - // Don't wrap in `ComponentId`, since that would just output: - // [ ComponentId(5), ComponentId(7) ] - f.debug_list().entries(self.0.ones()).finish() - } -} - -impl Clone for ComponentIdSet { - #[inline] - fn clone(&self) -> Self { - Self(self.0.clone()) - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - self.0.clone_from(&source.0); - } -} - -impl IntoIterator for ComponentIdSet { - type Item = ComponentId; - - type IntoIter = ComponentIdIter; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - ComponentIdIter(self.0.into_ones()) - } -} - -impl<'a> IntoIterator for &'a ComponentIdSet { - type Item = ComponentId; - - type IntoIter = ComponentIdIter>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl FromIterator for ComponentIdSet { - #[inline] - fn from_iter>(iter: T) -> Self { - Self(FixedBitSet::from_iter( - iter.into_iter().map(ComponentId::index), - )) - } -} - -impl Extend for ComponentIdSet { - #[inline] - fn extend>(&mut self, iter: T) { - self.0.extend(iter.into_iter().map(ComponentId::index)); - } -} - /// An iterator of [`ComponentId`]s. /// -/// This is equivalent to `map(ComponentId::new)`, +/// This is equivalent to `map(ComponentId::from_u32)`, /// but is a named type to allow it to be used in associated types. +// TODO: Maybe remove this entirely #[repr(transparent)] pub struct ComponentIdIter(I); @@ -1475,7 +1295,7 @@ impl> Iterator for ComponentIdIter { #[inline] fn next(&mut self) -> Option { - self.0.next().map(ComponentId::new) + self.0.next().map(|id| ComponentId::from_u32(id as u32)) } #[inline] @@ -1487,7 +1307,9 @@ impl> Iterator for ComponentIdIter { impl> DoubleEndedIterator for ComponentIdIter { #[inline] fn next_back(&mut self) -> Option { - self.0.next_back().map(ComponentId::new) + self.0 + .next_back() + .map(|id| ComponentId::from_u32(id as u32)) } } @@ -1497,10 +1319,10 @@ impl> FusedIterator for ComponentIdIter {} mod tests { use super::{invertible_difference_with, invertible_union_with}; use crate::{ - component::ComponentId, + component::{ComponentId, ComponentIdSet}, query::{ - access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, ComponentIdSet, - FilteredAccess, FilteredAccessSet, UnboundedAccessError, + access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, FilteredAccess, + FilteredAccessSet, UnboundedAccessError, }, }; use alloc::{vec, vec::Vec}; @@ -1509,10 +1331,10 @@ mod tests { fn create_sample_access() -> Access { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); - access.add_write(ComponentId::new(3)); - access.add_archetypal(ComponentId::new(5)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); + access.add_write(ComponentId::from_u32(3)); + access.add_archetypal(ComponentId::from_u32(5)); access.read_all(); access @@ -1521,10 +1343,10 @@ mod tests { fn create_sample_filtered_access() -> FilteredAccess { let mut filtered_access = FilteredAccess::default(); - filtered_access.add_write(ComponentId::new(1)); - filtered_access.add_read(ComponentId::new(2)); - filtered_access.add_required(ComponentId::new(3)); - filtered_access.and_with(ComponentId::new(4)); + filtered_access.add_write(ComponentId::from_u32(1)); + filtered_access.add_read(ComponentId::from_u32(2)); + filtered_access.add_required(ComponentId::from_u32(3)); + filtered_access.and_with(ComponentId::from_u32(4)); filtered_access } @@ -1532,8 +1354,8 @@ mod tests { fn create_sample_access_filters() -> AccessFilters { let mut access_filters = AccessFilters::default(); - access_filters.with.insert(ComponentId::new(3)); - access_filters.without.insert(ComponentId::new(5)); + access_filters.with.insert(ComponentId::from_u32(3)); + access_filters.without.insert(ComponentId::from_u32(5)); access_filters } @@ -1541,8 +1363,8 @@ mod tests { fn create_sample_filtered_access_set() -> FilteredAccessSet { let mut filtered_access_set = FilteredAccessSet::default(); - filtered_access_set.add_unfiltered_component_read(ComponentId::new(2)); - filtered_access_set.add_unfiltered_component_write(ComponentId::new(4)); + filtered_access_set.add_unfiltered_component_read(ComponentId::from_u32(2)); + filtered_access_set.add_unfiltered_component_write(ComponentId::from_u32(4)); filtered_access_set.read_all(); filtered_access_set @@ -1561,9 +1383,9 @@ mod tests { let original = create_sample_access(); let mut cloned = Access::default(); - cloned.add_write(ComponentId::new(7)); - cloned.add_read(ComponentId::new(4)); - cloned.add_archetypal(ComponentId::new(8)); + cloned.add_write(ComponentId::from_u32(7)); + cloned.add_read(ComponentId::from_u32(4)); + cloned.add_archetypal(ComponentId::from_u32(8)); cloned.write_all(); cloned.clone_from(&original); @@ -1584,8 +1406,8 @@ mod tests { let original = create_sample_filtered_access(); let mut cloned = FilteredAccess::default(); - cloned.add_write(ComponentId::new(7)); - cloned.add_read(ComponentId::new(4)); + cloned.add_write(ComponentId::from_u32(7)); + cloned.add_read(ComponentId::from_u32(4)); cloned.append_or(&FilteredAccess::default()); cloned.clone_from(&original); @@ -1606,8 +1428,8 @@ mod tests { let original = create_sample_access_filters(); let mut cloned = AccessFilters::default(); - cloned.with.insert(ComponentId::new(1)); - cloned.without.insert(ComponentId::new(2)); + cloned.with.insert(ComponentId::from_u32(1)); + cloned.without.insert(ComponentId::from_u32(2)); cloned.clone_from(&original); @@ -1627,8 +1449,8 @@ mod tests { let original = create_sample_filtered_access_set(); let mut cloned = FilteredAccessSet::default(); - cloned.add_unfiltered_component_read(ComponentId::new(7)); - cloned.add_unfiltered_component_write(ComponentId::new(9)); + cloned.add_unfiltered_component_read(ComponentId::from_u32(7)); + cloned.add_unfiltered_component_write(ComponentId::from_u32(9)); cloned.write_all(); cloned.clone_from(&original); @@ -1640,7 +1462,7 @@ mod tests { fn read_all_access_conflicts() { // read_all / single write let mut access_a = Access::default(); - access_a.add_write(ComponentId::new(0)); + access_a.add_write(ComponentId::from_u32(0)); let mut access_b = Access::default(); access_b.read_all(); @@ -1660,54 +1482,54 @@ mod tests { #[test] fn access_get_conflicts() { let mut access_a = Access::default(); - access_a.add_read(ComponentId::new(0)); - access_a.add_read(ComponentId::new(1)); + access_a.add_read(ComponentId::from_u32(0)); + access_a.add_read(ComponentId::from_u32(1)); let mut access_b = Access::default(); - access_b.add_read(ComponentId::new(0)); - access_b.add_write(ComponentId::new(1)); + access_b.add_read(ComponentId::from_u32(0)); + access_b.add_write(ComponentId::from_u32(1)); assert_eq!( access_a.get_conflicts(&access_b), - vec![ComponentId::new(1)].into() + vec![ComponentId::from_u32(1)].into() ); let mut access_c = Access::default(); - access_c.add_write(ComponentId::new(0)); - access_c.add_write(ComponentId::new(1)); + access_c.add_write(ComponentId::from_u32(0)); + access_c.add_write(ComponentId::from_u32(1)); assert_eq!( access_a.get_conflicts(&access_c), - vec![ComponentId::new(0), ComponentId::new(1)].into() + vec![ComponentId::from_u32(0), ComponentId::from_u32(1)].into() ); assert_eq!( access_b.get_conflicts(&access_c), - vec![ComponentId::new(0), ComponentId::new(1)].into() + vec![ComponentId::from_u32(0), ComponentId::from_u32(1)].into() ); let mut access_d = Access::default(); - access_d.add_read(ComponentId::new(0)); + access_d.add_read(ComponentId::from_u32(0)); assert_eq!(access_d.get_conflicts(&access_a), AccessConflicts::empty()); assert_eq!(access_d.get_conflicts(&access_b), AccessConflicts::empty()); assert_eq!( access_d.get_conflicts(&access_c), - vec![ComponentId::new(0)].into() + vec![ComponentId::from_u32(0)].into() ); } #[test] fn filtered_combined_access() { let mut access_a = FilteredAccessSet::default(); - access_a.add_unfiltered_component_read(ComponentId::new(1)); + access_a.add_unfiltered_component_read(ComponentId::from_u32(1)); let mut filter_b = FilteredAccess::default(); - filter_b.add_write(ComponentId::new(1)); + filter_b.add_write(ComponentId::from_u32(1)); let conflicts = access_a.get_conflicts_single(&filter_b); assert_eq!( &conflicts, - &AccessConflicts::from(vec![ComponentId::new(1)]), + &AccessConflicts::from(vec![ComponentId::from_u32(1)]), "access_a: {access_a:?}, filter_b: {filter_b:?}" ); } @@ -1715,23 +1537,23 @@ mod tests { #[test] fn filtered_access_extend() { let mut access_a = FilteredAccess::default(); - access_a.add_read(ComponentId::new(0)); - access_a.add_read(ComponentId::new(1)); - access_a.and_with(ComponentId::new(2)); + access_a.add_read(ComponentId::from_u32(0)); + access_a.add_read(ComponentId::from_u32(1)); + access_a.and_with(ComponentId::from_u32(2)); let mut access_b = FilteredAccess::default(); - access_b.add_read(ComponentId::new(0)); - access_b.add_write(ComponentId::new(3)); - access_b.and_without(ComponentId::new(4)); + access_b.add_read(ComponentId::from_u32(0)); + access_b.add_write(ComponentId::from_u32(3)); + access_b.and_without(ComponentId::from_u32(4)); access_a.extend(&access_b); let mut expected = FilteredAccess::default(); - expected.add_read(ComponentId::new(0)); - expected.add_read(ComponentId::new(1)); - expected.and_with(ComponentId::new(2)); - expected.add_write(ComponentId::new(3)); - expected.and_without(ComponentId::new(4)); + expected.add_read(ComponentId::from_u32(0)); + expected.add_read(ComponentId::from_u32(1)); + expected.and_with(ComponentId::from_u32(2)); + expected.add_write(ComponentId::from_u32(3)); + expected.and_without(ComponentId::from_u32(4)); assert!(access_a.eq(&expected)); } @@ -1740,17 +1562,17 @@ mod tests { fn filtered_access_extend_or() { let mut access_a = FilteredAccess::default(); // Exclusive access to `(&mut A, &mut B)`. - access_a.add_write(ComponentId::new(0)); - access_a.add_write(ComponentId::new(1)); + access_a.add_write(ComponentId::from_u32(0)); + access_a.add_write(ComponentId::from_u32(1)); // Filter by `With`. let mut access_b = FilteredAccess::default(); - access_b.and_with(ComponentId::new(2)); + access_b.and_with(ComponentId::from_u32(2)); // Filter by `(With, Without)`. let mut access_c = FilteredAccess::default(); - access_c.and_with(ComponentId::new(3)); - access_c.and_without(ComponentId::new(4)); + access_c.and_with(ComponentId::from_u32(3)); + access_c.and_without(ComponentId::from_u32(4)); // Turns `access_b` into `Or<(With, (With, Without))>`. access_b.append_or(&access_c); @@ -1762,8 +1584,8 @@ mod tests { // The intention here is to test that exclusive access implied by `add_write` // forms correct normalized access structs when extended with `Or` filters. let mut expected = FilteredAccess::default(); - expected.add_write(ComponentId::new(0)); - expected.add_write(ComponentId::new(1)); + expected.add_write(ComponentId::from_u32(0)); + expected.add_write(ComponentId::from_u32(1)); // The resulted access is expected to represent `Or<((With, With, With), (With, With, With, Without))>`. expected.filter_sets = vec![ AccessFilters { @@ -1786,20 +1608,20 @@ mod tests { fn try_iter_component_access_simple() { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); - access.add_write(ComponentId::new(3)); - access.add_archetypal(ComponentId::new(5)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); + access.add_write(ComponentId::from_u32(3)); + access.add_archetypal(ComponentId::from_u32(5)); let result = access.try_iter_access().map(Iterator::collect::>); assert_eq!( result, Ok(vec![ - ComponentAccessKind::Shared(ComponentId::new(1)), - ComponentAccessKind::Shared(ComponentId::new(2)), - ComponentAccessKind::Exclusive(ComponentId::new(3)), - ComponentAccessKind::Archetypal(ComponentId::new(5)), + ComponentAccessKind::Shared(ComponentId::from_u32(1)), + ComponentAccessKind::Shared(ComponentId::from_u32(2)), + ComponentAccessKind::Exclusive(ComponentId::from_u32(3)), + ComponentAccessKind::Archetypal(ComponentId::from_u32(5)), ]), ); } @@ -1808,8 +1630,8 @@ mod tests { fn try_iter_component_access_unbounded_write_all() { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); access.write_all(); let result = access.try_iter_access().map(Iterator::collect::>); @@ -1827,8 +1649,8 @@ mod tests { fn try_iter_component_access_unbounded_read_all() { let mut access = Access::default(); - access.add_read(ComponentId::new(1)); - access.add_read(ComponentId::new(2)); + access.add_read(ComponentId::from_u32(1)); + access.add_read(ComponentId::from_u32(2)); access.read_all(); let result = access.try_iter_access().map(Iterator::collect::>); @@ -1842,20 +1664,21 @@ mod tests { ); } - /// Create a `ComponentIdSet` with a given number of total bits and a given list of bits to set. - /// Setting the number of bits is important in tests since the `PartialEq` impl checks that the length matches. - fn bit_set(bits: usize, iter: impl IntoIterator) -> ComponentIdSet { - let mut result = FixedBitSet::with_capacity(bits); - result.extend(iter); - ComponentIdSet::from_bits(result) + /// Create a `HashSet` with a given list of `ComponentId`s to set. + fn hashset_from_u32s(array: [u32; N]) -> ComponentIdSet { + let mut set = ComponentIdSet::new(); + for index in array { + set.insert(ComponentId::from_u32(index)); + } + set } #[test] fn invertible_union_with_tests() { let invertible_union = |mut self_inverted: bool, other_inverted: bool| { // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); + let mut self_set = hashset_from_u32s([0, 1]); + let other_set = hashset_from_u32s([0, 2]); invertible_union_with( &mut self_set, &mut self_inverted, @@ -1868,19 +1691,25 @@ mod tests { // Check each combination of `inverted` flags let (s, i) = invertible_union(false, false); // [0, 1] | [0, 2] = [0, 1, 2] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), false)); + assert_eq!((s, i), (hashset_from_u32s([0, 1, 2]), false)); let (s, i) = invertible_union(false, true); // [0, 1] | [1, 3, ...] = [0, 1, 3, ...] - assert_eq!((s, i), (bit_set(4, [2]), true)); + assert_eq!((s, i), (hashset_from_u32s([2]), true)); let (s, i) = invertible_union(true, false); // [2, 3, ...] | [0, 2] = [0, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [1]), true)); + assert_eq!((s, i), (hashset_from_u32s([1]), true)); let (s, i) = invertible_union(true, true); // [2, 3, ...] | [1, 3, ...] = [1, 2, 3, ...] - assert_eq!((s, i), (bit_set(4, [0]), true)); + assert_eq!( + (s, i), + ( + ComponentIdSet::from_bits(FixedBitSet::with_capacity_and_blocks(2, [0b01])), + true + ) + ); } #[test] @@ -1889,9 +1718,9 @@ mod tests { // make sure we invert the bits beyond the original length. // Failing to call `grow` before `toggle_range` would cause bit 1 to be zero, // which would incorrectly treat it as included in the output set. - let mut self_set = bit_set(1, [0]); + let mut self_set = hashset_from_u32s([0]); let mut self_inverted = false; - let other_set = bit_set(3, [0, 1]); + let other_set = hashset_from_u32s([0, 1]); let other_inverted = true; invertible_union_with( &mut self_set, @@ -1901,15 +1730,15 @@ mod tests { ); // [0] | [2, ...] = [0, 2, ...] - assert_eq!((self_set, self_inverted), (bit_set(3, [1]), true)); + assert_eq!((self_set, self_inverted), (hashset_from_u32s([1]), true)); } #[test] fn invertible_difference_with_tests() { let invertible_difference = |mut self_inverted: bool, other_inverted: bool| { // Check all four possible bit states: In both sets, the first, the second, or neither - let mut self_set = bit_set(4, [0, 1]); - let other_set = bit_set(4, [0, 2]); + let mut self_set = hashset_from_u32s([0, 1]); + let other_set = hashset_from_u32s([0, 2]); invertible_difference_with( &mut self_set, &mut self_inverted, @@ -1922,66 +1751,72 @@ mod tests { // Check each combination of `inverted` flags let (s, i) = invertible_difference(false, false); // [0, 1] - [0, 2] = [1] - assert_eq!((s, i), (bit_set(4, [1]), false)); + assert_eq!((s, i), (hashset_from_u32s([1]), false)); let (s, i) = invertible_difference(false, true); // [0, 1] - [1, 3, ...] = [0] - assert_eq!((s, i), (bit_set(4, [0]), false)); + assert_eq!( + (s, i), + ( + ComponentIdSet::from_bits(FixedBitSet::with_capacity_and_blocks(2, [0b01])), + false + ) + ); let (s, i) = invertible_difference(true, false); // [2, 3, ...] - [0, 2] = [3, ...] - assert_eq!((s, i), (bit_set(4, [0, 1, 2]), true)); + assert_eq!((s, i), (hashset_from_u32s([0, 1, 2]), true)); let (s, i) = invertible_difference(true, true); // [2, 3, ...] - [1, 3, ...] = [2] - assert_eq!((s, i), (bit_set(4, [2]), false)); + assert_eq!((s, i), (hashset_from_u32s([2]), false)); } #[test] fn component_id_set_insert_remove_clear() { let mut set = ComponentIdSet::new(); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(!set.contains(ComponentId::new(2))); + assert!(!set.contains(ComponentId::from_u32(0))); + assert!(!set.contains(ComponentId::from_u32(1))); + assert!(!set.contains(ComponentId::from_u32(2))); assert!(set.is_clear()); - set.insert(ComponentId::new(2)); - set.insert(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); + set.insert(ComponentId::from_u32(2)); + set.insert(ComponentId::from_u32(1)); + assert!(!set.contains(ComponentId::from_u32(0))); + assert!(set.contains(ComponentId::from_u32(1))); + assert!(set.contains(ComponentId::from_u32(2))); assert!(!set.is_clear()); - set.remove(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); + set.remove(ComponentId::from_u32(1)); + assert!(!set.contains(ComponentId::from_u32(0))); + assert!(!set.contains(ComponentId::from_u32(1))); + assert!(set.contains(ComponentId::from_u32(2))); assert!(!set.is_clear()); - set.insert(ComponentId::new(2)); - set.insert(ComponentId::new(1)); - assert!(!set.contains(ComponentId::new(0))); - assert!(set.contains(ComponentId::new(1))); - assert!(set.contains(ComponentId::new(2))); + set.insert(ComponentId::from_u32(2)); + set.insert(ComponentId::from_u32(1)); + assert!(!set.contains(ComponentId::from_u32(0))); + assert!(set.contains(ComponentId::from_u32(1))); + assert!(set.contains(ComponentId::from_u32(2))); assert!(!set.is_clear()); set.clear(); - assert!(!set.contains(ComponentId::new(0))); - assert!(!set.contains(ComponentId::new(1))); - assert!(!set.contains(ComponentId::new(2))); + assert!(!set.contains(ComponentId::from_u32(0))); + assert!(!set.contains(ComponentId::from_u32(1))); + assert!(!set.contains(ComponentId::from_u32(2))); assert!(set.is_clear()); } #[test] fn component_id_set_remove_out_of_range() { let mut set = ComponentIdSet::new(); - set.remove(ComponentId::new(3)); - set.insert(ComponentId::new(1)); - set.remove(ComponentId::new(4)); - assert!(set.iter().eq([1].map(ComponentId::new))); + set.remove(ComponentId::from_u32(3)); + set.insert(ComponentId::from_u32(1)); + set.remove(ComponentId::from_u32(4)); + assert!(set.iter().eq([1].map(ComponentId::from_u32))); } #[test] fn component_id_set_is_subset_is_disjoint() { - let set_1234 = ComponentIdSet::from_iter([1, 2, 3, 4].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); - let set_45 = ComponentIdSet::from_iter([4, 5].map(ComponentId::new)); + let set_1234 = ComponentIdSet::from_iter([1, 2, 3, 4].map(ComponentId::from_u32)); + let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::from_u32)); + let set_45 = ComponentIdSet::from_iter([4, 5].map(ComponentId::from_u32)); assert!(set_23.is_subset(&set_1234)); assert!(!set_1234.is_subset(&set_23)); assert!(set_23.is_disjoint(&set_45)); @@ -1992,52 +1827,64 @@ mod tests { #[test] fn component_id_set_union_intersection_difference() { - let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); - - assert!(set_13.union(&set_23).eq([1, 3, 2].map(ComponentId::new))); - assert!(set_23.union(&set_13).eq([2, 3, 1].map(ComponentId::new))); - assert!(set_13.intersection(&set_23).eq([3].map(ComponentId::new))); - assert!(set_23.intersection(&set_13).eq([3].map(ComponentId::new))); - assert!(set_13.difference(&set_23).eq([1].map(ComponentId::new))); - assert!(set_23.difference(&set_13).eq([2].map(ComponentId::new))); + let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::from_u32)); + let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::from_u32)); + + assert!(set_13 + .union(&set_23) + .eq([1, 3, 2].map(ComponentId::from_u32))); + assert!(set_23 + .union(&set_13) + .eq([2, 3, 1].map(ComponentId::from_u32))); + assert!(set_13 + .intersection(&set_23) + .eq([3].map(ComponentId::from_u32))); + assert!(set_23 + .intersection(&set_13) + .eq([3].map(ComponentId::from_u32))); + assert!(set_13 + .difference(&set_23) + .eq([1].map(ComponentId::from_u32))); + assert!(set_23 + .difference(&set_13) + .eq([2].map(ComponentId::from_u32))); } #[test] fn component_id_set_union_intersection_difference_with() { - let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::new)); - let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::new)); + let set_13 = ComponentIdSet::from_iter([1, 3].map(ComponentId::from_u32)); + let set_23 = ComponentIdSet::from_iter([2, 3].map(ComponentId::from_u32)); let mut s = set_13.clone(); s.union_with(&set_23); - assert!(s.iter().eq([1, 2, 3].map(ComponentId::new))); + assert!(s.iter().eq([1, 2, 3].map(ComponentId::from_u32))); let mut s = set_23.clone(); s.union_with(&set_13); - assert!(s.iter().eq([1, 2, 3].map(ComponentId::new))); + assert!(s.iter().eq([1, 2, 3].map(ComponentId::from_u32))); let mut s = set_13.clone(); s.intersect_with(&set_23); - assert!(s.iter().eq([3].map(ComponentId::new))); + assert!(s.iter().eq([3].map(ComponentId::from_u32))); let mut s = set_23.clone(); s.intersect_with(&set_13); - assert!(s.iter().eq([3].map(ComponentId::new))); + assert!(s.iter().eq([3].map(ComponentId::from_u32))); let mut s = set_13.clone(); s.difference_with(&set_23); - assert!(s.iter().eq([1].map(ComponentId::new))); + assert!(s.iter().eq([1].map(ComponentId::from_u32))); let mut s = set_23.clone(); s.difference_with(&set_13); - assert!(s.iter().eq([2].map(ComponentId::new))); + assert!(s.iter().eq([2].map(ComponentId::from_u32))); let mut s = set_13.clone(); s.difference_from(&set_23); - assert!(s.iter().eq([2].map(ComponentId::new))); + assert!(s.iter().eq([2].map(ComponentId::from_u32))); let mut s = set_23.clone(); s.difference_from(&set_13); - assert!(s.iter().eq([1].map(ComponentId::new))); + assert!(s.iter().eq([1].map(ComponentId::from_u32))); } } diff --git a/crates/bevy_ecs/src/query/access_iter.rs b/crates/bevy_ecs/src/query/access_iter.rs index 0e8979e648eef..35e2b7019dc56 100644 --- a/crates/bevy_ecs/src/query/access_iter.rs +++ b/crates/bevy_ecs/src/query/access_iter.rs @@ -108,7 +108,7 @@ fn has_conflicts_large<'a, Q: QueryData>( } /// The data storage type that is being accessed. -#[derive(Copy, Clone, Debug, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum EcsAccessType<'a> { /// Accesses [`Component`](crate::prelude::Component) data Component(EcsAccessLevel), diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index 3e8099f53b057..7fccf21455fcc 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -4,16 +4,13 @@ use log::warn; use crate::{ component::{Component, ComponentId, Mutable}, - entity::Entity, lifecycle::HookContext, - storage::SparseArray, world::DeferredWorld, }; #[cfg(feature = "bevy_reflect")] use {crate::reflect::ReflectComponent, bevy_reflect::Reflect}; // The derive macro for the `Resource` trait pub use bevy_ecs_macros::Resource; -use bevy_platform::cell::SyncUnsafeCell; /// A type that can be inserted into a [`World`] as a singleton. /// @@ -86,37 +83,6 @@ use bevy_platform::cell::SyncUnsafeCell; )] pub trait Resource: Component {} -/// A cache that links each `ComponentId` from a resource to the corresponding entity. -#[derive(Default)] -pub struct ResourceEntities(SyncUnsafeCell>); - -impl ResourceEntities { - /// Returns an iterator over all registered resource components and their corresponding entity. - /// - /// This must scan the entire array of components to find non-empty values, - /// which may be slow even if there are few resources. - #[inline] - pub fn iter(&self) -> impl Iterator { - self.deref().iter().map(|(id, entity)| (id, *entity)) - } - - /// Returns the entity for the given resource component, or `None` if there is no entity. - #[inline] - pub fn get(&self, id: ComponentId) -> Option { - self.deref().get(id).copied() - } - - #[inline] - fn deref(&self) -> &SparseArray { - // SAFETY: There are no other mutable references to the map. - // The underlying `SyncUnsafeCell` is never exposed outside this module, - // so mutable references are only created by the resource hooks. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &*self.0.get() } - } -} - /// A marker component for entities that have a Resource component. #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))] #[derive(Component, Debug)] @@ -140,44 +106,36 @@ impl IsResource { .get::() .unwrap() .resource_component_id(); + let original_entity = resource_component_id.id(); + + if !world.entities().contains(original_entity) { + let name = world + .components() + .get_name(resource_component_id) + .expect("resource is registered"); + panic!( + "Resource entity {} of {} has been despawned, when it's not supposed to be.", + original_entity, name + ); + } - if let Some(original_entity) = world.resource_entities.get(resource_component_id) { - if !world.entities().contains(original_entity) { - let name = world - .components() - .get_name(resource_component_id) - .expect("resource is registered"); - panic!( - "Resource entity {} of {} has been despawned, when it's not supposed to be.", - original_entity, name - ); - } - - if original_entity != context.entity { - // the resource already exists and the new one should be removed - world - .commands() - .entity(context.entity) - .remove_by_id(resource_component_id); - world - .commands() - .entity(context.entity) - .remove_by_id(context.component_id); - let name = world - .components() - .get_name(resource_component_id) - .expect("resource is registered"); - warn!("Tried inserting the resource {} while one already exists. + if original_entity != context.entity { + // the resource already exists and the new one should be removed + world + .commands() + .entity(context.entity) + .remove_by_id(resource_component_id); + world + .commands() + .entity(context.entity) + .remove_by_id(context.component_id); + let name = world + .components() + .get_name(resource_component_id) + .expect("resource is registered"); + warn!("Tried inserting the resource {} while one already exists. Resources are unique components stored on a single entity. Inserting on a different entity, when one already exists, causes the new value to be removed.", name); - } - } else { - // SAFETY: We have exclusive world access (as long as we don't make structural changes). - let cache = unsafe { world.as_unsafe_world_cell().resource_entities() }; - // SAFETY: There are no shared references to the map. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &mut *cache.0.get() }.insert(resource_component_id, context.entity); } } @@ -187,17 +145,9 @@ impl IsResource { .get::() .unwrap() .resource_component_id(); + let original_entity = resource_component_id.id(); - if let Some(resource_entity) = world.resource_entities.get(resource_component_id) - && resource_entity == context.entity - { - // SAFETY: We have exclusive world access (as long as we don't make structural changes). - let cache = unsafe { world.as_unsafe_world_cell().resource_entities() }; - // SAFETY: There are no shared references to the map. - // We only expose `&ResourceCache` to code with access to a resource (such as `&World`), - // and that would conflict with the `DeferredWorld` passed to the resource hook. - unsafe { &mut *cache.0.get() }.remove(resource_component_id); - + if original_entity == context.entity { world .commands() .entity(context.entity) @@ -211,7 +161,7 @@ impl IsResource { } /// [`ComponentId`] of the [`IsResource`] component. -pub const IS_RESOURCE: ComponentId = ComponentId::new(crate::component::IS_RESOURCE); +pub const IS_RESOURCE: ComponentId = ComponentId::from_u32(crate::component::IS_RESOURCE); #[cfg(test)] mod tests { @@ -255,7 +205,7 @@ mod tests { } }); assert_eq!(world.entities().count_spawned(), start + 3); - let e3 = world.resource_entities().get(id3).unwrap(); + let e3 = id3.id(); assert!(world.remove_resource_by_id(id3)); // the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity assert_eq!(world.entities().count_spawned(), start + 3); @@ -265,13 +215,13 @@ mod tests { world.insert_resource_by_id(id3, ptr, MaybeLocation::caller()); } }); - assert_eq!(e3, world.resource_entities().get(id3).unwrap()); + assert_eq!(e3, id3.id()); // again, the entity is stable: see previous explanation - let e1 = world.resource_entities().get(id1).unwrap(); + let e1 = id1.id(); world.remove_resource::(); assert_eq!(world.entities().count_spawned(), start + 3); world.init_resource::(); - assert_eq!(e1, world.resource_entities().get(id1).unwrap()); + assert_eq!(e1, id1.id()); // make sure that trying to add a resource twice results, doesn't change the entity count world.insert_resource(TestResource2(String::from("Bar"))); assert_eq!(world.entities().count_spawned(), start + 3); @@ -292,7 +242,9 @@ mod tests { let resources = query.iter(&world).collect::>(); assert_eq!(resources.len(), 1); let (entity, _test_resource, is_resource) = resources[0]; - assert_eq!(is_resource.resource_component_id(), id); + let resource_id = is_resource.resource_component_id(); + assert_eq!(resource_id, id); + assert_eq!(resource_id.id(), entity); entity }; @@ -311,13 +263,15 @@ mod tests { let resources = query.iter(&world).collect::>(); assert_eq!(resources.len(), 1); let (entity, _test_resource, is_resource) = resources[0]; - assert_eq!(is_resource.resource_component_id(), id); + let resource_id = is_resource.resource_component_id(); + assert_eq!(resource_id, id); + assert_eq!(resource_id.id(), entity); entity }; - assert_ne!( + assert_eq!( first_entity, second_entity, - "The first resource entity was invalidated, so the second initialization should be new" + "The entity on which a resource is spawned is always the same." ); let id = world.spawn(TestResource).id(); diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 13460e2defd1e..26fa261b20f0e 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -61,6 +61,10 @@ impl SparseArray { marker: PhantomData, } } + + pub fn len(&self) -> usize { + self.values.len() + } } macro_rules! impl_sparse_array { @@ -141,12 +145,8 @@ impl SparseArray { /// This must scan the entire array to find non-empty values, /// which may be slow even if the array is sparsely populated. #[inline] - pub(crate) fn iter(&self) -> impl Iterator { - self.values.iter().enumerate().filter_map(|(index, value)| { - value - .as_ref() - .map(|value| (SparseSetIndex::get_sparse_set_index(index), value)) - }) + pub(crate) fn values(&self) -> impl Iterator { + self.values.iter().filter_map(|a| a.as_ref()) } } @@ -937,12 +937,12 @@ mod tests { collected_sets.sort(); assert_eq!( collected_sets, - vec![(ComponentId::new(1), 0), (ComponentId::new(2), 0),] + vec![(ComponentId::from_u32(2), 0), (ComponentId::from_u32(1), 0),] ); - fn register_component(sets: &mut SparseSets, id: usize) { + fn register_component(sets: &mut SparseSets, id: u32) { let descriptor = ComponentDescriptor::new::(); - let id = ComponentId::new(id); + let id = ComponentId::from_u32(id); let info = ComponentInfo::new(id, descriptor); sets.get_or_insert(&info); } diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index 494260c463b34..4a5b1e847ec9b 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -857,8 +857,8 @@ impl Drop for Table { mod tests { use crate::{ change_detection::{MaybeLocation, Tick}, - component::{Component, ComponentIds, Components, ComponentsRegistrator}, - entity::{Entity, EntityIndex}, + component::{Component, Components, ComponentsRegistrator}, + entity::{Entity, EntityAllocator, EntityIndex}, ptr::OwningPtr, storage::{TableBuilder, TableId, TableRow, Tables}, }; @@ -882,10 +882,10 @@ mod tests { #[test] fn table() { let mut components = Components::default(); - let mut componentids = ComponentIds::default(); + let mut allocator = EntityAllocator::default(); // SAFETY: They are both new. let mut registrator = - unsafe { ComponentsRegistrator::new(&mut components, &mut componentids) }; + unsafe { ComponentsRegistrator::new(&mut components, &mut allocator) }; let component_id = registrator.register_component::>(); let columns = &[component_id]; let mut table = TableBuilder::with_capacity(0, columns.len()) diff --git a/crates/bevy_ecs/src/world/entity_access/mod.rs b/crates/bevy_ecs/src/world/entity_access/mod.rs index a4a63e76d2cf3..7e2b1465fcf40 100644 --- a/crates/bevy_ecs/src/world/entity_access/mod.rs +++ b/crates/bevy_ecs/src/world/entity_access/mod.rs @@ -99,7 +99,7 @@ mod tests { #[test] fn entity_ref_get_by_id_invalid_component_id() { - let invalid_component_id = ComponentId::new(usize::MAX); + let invalid_component_id = ComponentId::from_u32(u32::MAX - 1); let mut world = World::new(); let entity = world.spawn_empty().id(); @@ -109,7 +109,7 @@ mod tests { #[test] fn entity_mut_get_by_id_invalid_component_id() { - let invalid_component_id = ComponentId::new(usize::MAX); + let invalid_component_id = ComponentId::from_u32(u32::MAX - 1); let mut world = World::new(); let mut entity = world.spawn_empty(); diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 8ee75de95f147..92ed07a2d2973 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -44,7 +44,7 @@ use crate::{ CheckChangeTicks, ComponentTicks, ComponentTicksMut, MaybeLocation, MutUntyped, Tick, }, component::{ - Component, ComponentDescriptor, ComponentId, ComponentIds, ComponentInfo, Components, + Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable, RequiredComponents, RequiredComponentsError, }, @@ -57,7 +57,7 @@ use crate::{ prelude::{Add, Despawn, DetectChangesMut, Discard, Insert, Remove}, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, relationship::RelationshipHookMode, - resource::{IsResource, Resource, ResourceEntities, IS_RESOURCE}, + resource::{IsResource, Resource, IS_RESOURCE}, schedule::{Schedule, ScheduleLabel, Schedules}, storage::{NonSendData, Storages}, system::Commands, @@ -100,8 +100,6 @@ pub struct World { pub(crate) entities: Entities, pub(crate) entity_allocator: EntityAllocator, pub(crate) components: Components, - pub(crate) component_ids: ComponentIds, - pub(crate) resource_entities: ResourceEntities, pub(crate) archetypes: Archetypes, pub(crate) storages: Storages, pub(crate) bundles: Bundles, @@ -121,7 +119,6 @@ impl Default for World { entities: Entities::new(), entity_allocator: EntityAllocator::default(), components: Default::default(), - resource_entities: Default::default(), archetypes: Archetypes::new(), storages: Default::default(), bundles: Default::default(), @@ -134,7 +131,6 @@ impl Default for World { last_check_tick: Tick::new(0), last_trigger_id: 0, command_queue: RawCommandQueue::new(), - component_ids: ComponentIds::default(), }; world.bootstrap(); world @@ -259,26 +255,20 @@ impl World { &self.components } - /// Retrieves this world's [`ResourceEntities`]. - #[inline] - pub fn resource_entities(&self) -> &ResourceEntities { - &self.resource_entities - } - /// Prepares a [`ComponentsQueuedRegistrator`] for the world. /// **NOTE:** [`ComponentsQueuedRegistrator`] is easily misused. /// See its docs for important notes on when and how it should be used. #[inline] pub fn components_queue(&self) -> ComponentsQueuedRegistrator<'_> { // SAFETY: These are from the same world. - unsafe { ComponentsQueuedRegistrator::new(&self.components, &self.component_ids) } + unsafe { ComponentsQueuedRegistrator::new(&self.components, &self.entity_allocator) } } /// Prepares a [`ComponentsRegistrator`] for the world. #[inline] pub fn components_registrator(&mut self) -> ComponentsRegistrator<'_> { // SAFETY: These are from the same world. - unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) } + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) } } /// Retrieves this world's [`Storages`] collection. @@ -1858,10 +1848,10 @@ impl World { func: impl FnOnce(&mut World) -> R, caller: MaybeLocation, ) -> (ComponentId, EntityWorldMut<'_>) { - let resource_id = self.register_resource::(); + let resource_id: ComponentId = self.register_resource::(); + let entity = resource_id.id(); - if let Some(entity) = self.resource_entities.get(resource_id) { - let entity_ref = self.get_entity(entity).expect("ResourceCache is in sync"); + if let Ok(entity_ref) = self.get_entity(entity) { if !entity_ref.contains_id(resource_id) { let resource = func(self); move_as_ptr!(resource); @@ -1877,7 +1867,7 @@ impl World { let resource = func(self); move_as_ptr!(resource); - let entity_mut = self.spawn_with_caller(resource, caller); // ResourceCache is updated automatically + let entity_mut = self.spawn_at_unchecked(entity, resource, caller); (resource_id, entity_mut) } @@ -1995,9 +1985,8 @@ impl World { #[inline] pub fn remove_resource(&mut self) -> Option { let resource_id = self.component_id::()?; - let entity = self.resource_entities.get(resource_id)?; let value = self - .get_entity_mut(entity) + .get_entity_mut(resource_id.id()) .expect("ResourceCache is in sync") .take::()?; Some(value) @@ -2039,9 +2028,7 @@ impl World { /// Returns `true` if a resource with provided `component_id` exists. Otherwise returns `false`. #[inline] pub fn contains_resource_by_id(&self, component_id: ComponentId) -> bool { - if let Some(entity) = self.resource_entities.get(component_id) - && let Ok(entity_ref) = self.get_entity(entity) - { + if let Ok(entity_ref) = self.get_entity(component_id.id()) { return entity_ref.contains_id(component_id); } false @@ -2129,8 +2116,7 @@ impl World { &self, component_id: ComponentId, ) -> Option { - let entity = self.resource_entities.get(component_id)?; - let entity_ref = self.get_entity(entity).ok()?; + let entity_ref = self.get_entity(component_id.id()).ok()?; entity_ref.get_change_ticks_by_id(component_id) } @@ -2781,7 +2767,7 @@ impl World { let change_tick = self.change_tick(); let component_id = self.components.valid_component_id::()?; - let entity = self.resource_entities.get(component_id)?; + let entity = component_id.id(); let mut entity_mut = self.get_entity_mut(entity).ok()?; let mut ticks = entity_mut.get_change_ticks::()?; @@ -2972,13 +2958,14 @@ impl World { value: OwningPtr<'_>, caller: MaybeLocation, ) { + let entity = component_id.id(); // if the resource already exists, we replace it on the same entity - let mut entity_mut = if let Some(entity) = self.resource_entities.get(component_id) { - self.get_entity_mut(entity) - .expect("ResourceCache is in sync") + let mut entity_mut = if let Ok(entity_mut) = self.get_entity_mut(entity) { + entity_mut } else { - self.spawn_empty() + self.spawn_empty_at(entity).unwrap() // doesn't already exist }; + entity_mut.insert_by_id_with_caller( component_id, value, @@ -3303,9 +3290,16 @@ impl World { /// This can easily cause systems expecting certain resources to immediately start panicking. /// Use with caution. pub fn clear_resources(&mut self) { - let pairs: Vec<(ComponentId, Entity)> = self.resource_entities().iter().collect(); - for (component_id, entity) in pairs { - self.entity_mut(entity).remove_by_id(component_id); + let ids: Vec = self + .components + .iter_registered() + .map(ComponentInfo::id) + .collect(); + for component_id in ids { + let entity = component_id.id(); + if let Ok(mut entity_mut) = self.get_entity_mut(entity) { + entity_mut.remove_by_id(component_id); + } } } @@ -3330,7 +3324,7 @@ impl World { pub(crate) fn register_bundle_info(&mut self) -> BundleId { // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. let mut registrator = - unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) }; // SAFETY: `registrator`, `self.storages` and `self.bundles` all come from this world. unsafe { @@ -3342,7 +3336,7 @@ impl World { pub(crate) fn register_contributed_bundle_info(&mut self) -> BundleId { // SAFETY: These come from the same world. `Self.components_registrator` can't be used since we borrow other fields too. let mut registrator = - unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.component_ids) }; + unsafe { ComponentsRegistrator::new(&mut self.components, &mut self.entity_allocator) }; // SAFETY: `registrator`, `self.bundles` and `self.storages` are all from this world. unsafe { @@ -3504,10 +3498,11 @@ impl World { /// ``` #[inline] pub fn iter_resources(&self) -> impl Iterator)> { - self.resource_entities - .iter() - .filter_map(|(component_id, entity)| { - let component_info = self.components().get_info(component_id)?; + self.components + .iter_registered() + .filter_map(|component_info| { + let component_id = component_info.id(); + let entity = component_id.id(); let entity_cell = self.get_entity(entity).ok()?; let resource = entity_cell.get_by_id(component_id).ok()?; Some((component_info, resource)) @@ -3581,17 +3576,13 @@ impl World { /// ``` pub fn iter_resources_mut(&mut self) -> impl Iterator)> { let unsafe_world = self.as_unsafe_world_cell(); - // SAFETY: exclusive world access to all resources - let resource_entities = unsafe { unsafe_world.resource_entities() }; let components = unsafe_world.components(); - resource_entities - .iter() - .filter_map(move |(component_id, entity)| { - // SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with its ID. - let component_info = - unsafe { components.get_info(component_id).debug_checked_unwrap() }; - + components + .iter_registered() + .filter_map(move |component_info| { + let component_id = component_info.id(); + let entity = component_id.id(); let entity_cell = unsafe_world.get_entity(entity).ok()?; // SAFETY: @@ -3653,8 +3644,8 @@ impl World { /// **You should prefer to use the typed API [`World::remove_resource`] where possible and only /// use this in cases where the actual types are not known at compile time.** pub fn remove_resource_by_id(&mut self, component_id: ComponentId) -> bool { - if let Some(entity) = self.resource_entities.get(component_id) - && let Ok(mut entity_mut) = self.get_entity_mut(entity) + let entity = component_id.id(); + if let Ok(mut entity_mut) = self.get_entity_mut(entity) && entity_mut.contains_id(component_id) { entity_mut.remove_by_id(component_id); @@ -4126,6 +4117,8 @@ mod tests { let mut iter = world.iter_resources(); + // The resources are given back in order of component registration + // but this is not always true let (info, ptr) = iter.next().unwrap(); assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource` diff --git a/crates/bevy_ecs/src/world/reflect.rs b/crates/bevy_ecs/src/world/reflect.rs index 0c7909d5bc4aa..464d5bc4c2c41 100644 --- a/crates/bevy_ecs/src/world/reflect.rs +++ b/crates/bevy_ecs/src/world/reflect.rs @@ -199,10 +199,13 @@ impl World { resource_id: ComponentId, reflected_resource: Box, ) { - if let Some(entity) = self.resource_entities().get(resource_id) { - self.entity_mut(entity).insert_reflect(reflected_resource); + let entity = resource_id.id(); + if let Ok(mut entity_mut) = self.get_entity_mut(entity) { + entity_mut.insert_reflect(reflected_resource); } else { - self.spawn_empty().insert_reflect(reflected_resource); + self.spawn_empty_at(entity) + .unwrap() + .insert_reflect(reflected_resource); } } } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 7a9c7a8fa3757..7c9bd247bd074 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -17,7 +17,7 @@ use crate::{ observer::Observers, prelude::Component, query::{DebugCheckedUnwrap, QueryAccessError, ReleaseStateQueryData, SingleEntityQueryData}, - resource::{Resource, ResourceEntities}, + resource::Resource, storage::{ComponentSparseSet, Storages, Table}, world::RawCommandQueue, }; @@ -288,17 +288,6 @@ impl<'w> UnsafeWorldCell<'w> { &unsafe { self.world_metadata() }.components } - /// Retrieves this world's resource-entity map. - /// - /// # Safety - /// The caller must have exclusive read or write access to the resources that are updated in the cache. - #[inline] - pub unsafe fn resource_entities(self) -> &'w ResourceEntities { - // SAFETY: - // - we only access world metadata - &unsafe { self.world_metadata() }.resource_entities - } - /// Retrieves this world's collection of [removed components](RemovedComponentMessages). pub fn removed_components(self) -> &'w RemovedComponentMessages { // SAFETY: @@ -465,7 +454,7 @@ impl<'w> UnsafeWorldCell<'w> { #[inline] pub unsafe fn get_resource_by_id(self, component_id: ComponentId) -> Option> { // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.id(); let entity_cell = self.get_entity(entity).ok()?; entity_cell.get_by_id(component_id) } @@ -574,7 +563,7 @@ impl<'w> UnsafeWorldCell<'w> { ) -> Option> { self.assert_allows_mutable_access(); // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.id(); let entity_cell = self.get_entity(entity).ok()?; entity_cell.get_mut_by_id(component_id).ok() } @@ -678,7 +667,7 @@ impl<'w> UnsafeWorldCell<'w> { component_id: ComponentId, ) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> { // SAFETY: We have permission to access the resource of `component_id`. - let entity = unsafe { self.resource_entities() }.get(component_id)?; + let entity = component_id.id(); let storage_type = self.components().get_info(component_id)?.storage_type(); let location = self.get_entity(entity).ok()?.location(); // SAFETY: diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 2f10c3397d6a0..d38fe3baa123f 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -2014,10 +2014,10 @@ fn get_resource_entity_pair( .components() .get_id(type_id) .ok_or(anyhow!("Resource not registered: `{}`", resource_path))?; - let entity = world - .resource_entities() - .get(component_id) - .ok_or(anyhow!("Resource entity does not exist."))?; + let entity = component_id.id(); + if !world.entities().contains_spawned(entity) { + return Err(anyhow!("Resource entity does not exist.")); + } Ok((entity, component_id)) } diff --git a/crates/bevy_settings/src/lib.rs b/crates/bevy_settings/src/lib.rs index 4b9c4eff84fae..4a217284fb523 100644 --- a/crates/bevy_settings/src/lib.rs +++ b/crates/bevy_settings/src/lib.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::{ change_detection::Tick, + component::ComponentId, reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, resource::Resource, system::{Command, Commands, Res, ResMut}, @@ -341,9 +342,7 @@ fn resources_to_toml( continue; }; - let Some(res_entity) = world.resource_entities().get(component_id) else { - continue; - }; + let res_entity = component_id.id(); let res_entity_ref = world.entity(res_entity); let Some(reflect) = cmp.reflect(res_entity_ref) else { continue; @@ -470,7 +469,7 @@ fn apply_settings_to_world( let reflect_component = ty.data::().unwrap(); let component_id = world.components().get_id(*tid); - let res_entity = component_id.and_then(|cid| world.resource_entities().get(cid)); + let res_entity = component_id.map(ComponentId::id); if let Some(res_entity) = res_entity { // Resource already exists, so apply toml properties to it. @@ -497,7 +496,8 @@ fn apply_settings_to_world( // The resource does not exist, so create a default. let reflect_default = ty.data::().unwrap(); let mut default_value = reflect_default.default(); - let mut res_entity = world.spawn_empty(); + let new_component_id = reflect_component.register_component(world); + let mut res_entity = world.spawn_empty_at(new_component_id.id()).unwrap(); if let Some(toml) = toml && let Some(value) = toml.get(settings_group) diff --git a/crates/bevy_world_serialization/src/dynamic_world.rs b/crates/bevy_world_serialization/src/dynamic_world.rs index 87bbdcbb8c154..e506190c5d9ad 100644 --- a/crates/bevy_world_serialization/src/dynamic_world.rs +++ b/crates/bevy_world_serialization/src/dynamic_world.rs @@ -181,13 +181,10 @@ impl DynamicWorld { .expect("ReflectComponent is depended on ReflectResource"); let resource_id = reflect_component.register_component(world); - - // check if the resource already exists, if not spawn it, otherwise override the value - let entity = if let Some(entity) = world.resource_entities().get(resource_id) { - entity - } else { - world.spawn_empty().id() - }; + let entity = resource_id.id(); + if !world.entities().contains_spawned(entity) { + let _ = world.spawn_empty_at(entity); + } SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { reflect_component.apply_or_insert_mapped( diff --git a/crates/bevy_world_serialization/src/dynamic_world_builder.rs b/crates/bevy_world_serialization/src/dynamic_world_builder.rs index 727d7adc9fecc..6670017edaeba 100644 --- a/crates/bevy_world_serialization/src/dynamic_world_builder.rs +++ b/crates/bevy_world_serialization/src/dynamic_world_builder.rs @@ -5,7 +5,7 @@ use crate::{DynamicEntity, DynamicWorld, WorldFilter}; use alloc::collections::BTreeMap; use bevy_ecs::resource::IS_RESOURCE; use bevy_ecs::{ - component::{Component, ComponentId}, + component::{Component, ComponentId, ComponentInfo}, entity_disabling::DefaultQueryFilters, prelude::Entity, reflect::{ReflectComponent, ReflectResource}, @@ -377,8 +377,16 @@ impl<'w> DynamicWorldBuilder<'w> { .components() .get_valid_id(TypeId::of::()); - for (component_id, entity) in self.original_world.resource_entities().iter() { - if Some(component_id) == original_world_dqf_id { + for component_id in self + .original_world + .components() + .iter_registered() + .map(ComponentInfo::id) + { + let entity = component_id.id(); + if Some(component_id) == original_world_dqf_id + || !self.original_world.entities().contains_spawned(entity) + { continue; } let mut extract_and_push = || { @@ -501,8 +509,8 @@ mod tests { assert_eq!(dynamic_world.entities.len(), 1); assert_eq!(dynamic_world.entities[0].entity, entity); assert_eq!(dynamic_world.entities[0].components.len(), 2); - assert!(dynamic_world.entities[0].components[0].represents::()); - assert!(dynamic_world.entities[0].components[1].represents::()); + assert!(dynamic_world.entities[0].components[0].represents::()); + assert!(dynamic_world.entities[0].components[1].represents::()); } #[test] diff --git a/crates/bevy_world_serialization/src/serde.rs b/crates/bevy_world_serialization/src/serde.rs index 64505eda4b8ee..ab8cdde410dfb 100644 --- a/crates/bevy_world_serialization/src/serde.rs +++ b/crates/bevy_world_serialization/src/serde.rs @@ -694,25 +694,25 @@ mod tests { ), }, entities: { - 4294967290: ( + 4294967279: ( components: { "bevy_world_serialization::serde::tests::FakeMesh3d": (Uuid("00000000-0000-0000-0000-000000000001")), }, ), - 4294967291: ( + 4294967281: ( components: { "bevy_world_serialization::serde::tests::Bar": (345), "bevy_world_serialization::serde::tests::Baz": (789), "bevy_world_serialization::serde::tests::Foo": (123), }, ), - 4294967292: ( + 4294967283: ( components: { "bevy_world_serialization::serde::tests::Bar": (345), "bevy_world_serialization::serde::tests::Foo": (123), }, ), - 4294967293: ( + 4294967285: ( components: { "bevy_world_serialization::serde::tests::Foo": (123), }, @@ -920,7 +920,7 @@ mod tests { assert_eq!( vec![ - 0, 1, 253, 255, 255, 255, 15, 1, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, + 0, 1, 245, 255, 255, 255, 15, 1, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, 100, 95, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, @@ -963,7 +963,7 @@ mod tests { assert_eq!( vec![ - 146, 128, 129, 206, 255, 255, 255, 253, 145, 129, 217, 51, 98, 101, 118, 121, 95, + 146, 128, 129, 206, 255, 255, 255, 245, 145, 129, 217, 51, 98, 101, 118, 121, 95, 119, 111, 114, 108, 100, 95, 115, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, diff --git a/crates/bevy_world_serialization/src/world_asset.rs b/crates/bevy_world_serialization/src/world_asset.rs index 458974e898533..043f025a636d2 100644 --- a/crates/bevy_world_serialization/src/world_asset.rs +++ b/crates/bevy_world_serialization/src/world_asset.rs @@ -5,7 +5,7 @@ use crate::{DynamicWorld, WorldInstanceSpawnError}; use bevy_asset::Asset; use bevy_ecs::resource::IS_RESOURCE; use bevy_ecs::{ - component::ComponentCloneBehavior, + component::{ComponentCloneBehavior, ComponentInfo}, entity::{Entity, EntityHashMap, SceneEntityMapper}, entity_disabling::DefaultQueryFilters, reflect::{AppTypeRegistry, ReflectComponent, ReflectResource}, @@ -75,8 +75,16 @@ impl WorldAsset { .get_id(TypeId::of::()); // Resources archetype - for (component_id, source_entity) in self.world.resource_entities().iter() { - if Some(component_id) == self_dqf_id { + for component_id in self + .world + .components() + .iter_registered() + .map(ComponentInfo::id) + { + let source_entity = component_id.id(); + if Some(component_id) == self_dqf_id + || !self.world.entities().contains_spawned(source_entity) + { continue; } if !world @@ -113,12 +121,10 @@ impl WorldAsset { .expect("ReflectComponent is depended on ReflectResource"); // check if the resource already exists in the other world, if not spawn it - let destination_entity = - if let Some(entity) = world.resource_entities().get(component_id) { - entity - } else { - world.spawn_empty().id() - }; + let destination_entity = component_id.id(); + if !world.entities().contains_spawned(destination_entity) { + let _ = world.spawn_empty_at(destination_entity); + } reflect_component.copy( &self.world,