Isolate component registration (#17671)

# Objective

Progresses #17569. The end goal here is to synchronize component
registration. See the other PR for details for the motivation behind
that.

For this PR specifically, the objective is to decouple `Components` from
`Storages`. What components are registered etc should have nothing to do
with what Storages looks like. Storages should only care about what
entity archetypes have been spawned.

## Solution

Previously, this was used to create sparse sets for relevant components
when those components were registered. Now, we do that when the
component is inserted/spawned.

This PR proposes doing that in `BundleInfo::new`, but there may be a
better place.

## Testing

In theory, this shouldn't have changed any functionality, so no new
tests were created. I'm not aware of any examples that make heavy use of
sparse set components either.

## Migration Guide

- Remove storages from functions where it is no longer needed.
- Note that SparseSets are no longer present for all registered sparse
set components, only those that have been spawned.

---------

Co-authored-by: SpecificProtagonist <vincentjunge@posteo.net>
Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
This commit is contained in:
ElliottjPierce 2025-02-05 14:59:30 -05:00 committed by GitHub
parent d0c0bad7b4
commit 1b2cf7d6cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 162 additions and 147 deletions

View File

@ -156,7 +156,6 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
<#ident as #bevy_ecs_path::component::Component>::register_required_components( <#ident as #bevy_ecs_path::component::Component>::register_required_components(
requiree, requiree,
components, components,
storages,
required_components, required_components,
inheritance_depth + 1, inheritance_depth + 1,
recursion_check_stack recursion_check_stack
@ -166,7 +165,6 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
Some(RequireFunc::Path(func)) => { Some(RequireFunc::Path(func)) => {
register_required.push(quote! { register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>( components.register_required_components_manual::<Self, #ident>(
storages,
required_components, required_components,
|| { let x: #ident = #func().into(); x }, || { let x: #ident = #func().into(); x },
inheritance_depth, inheritance_depth,
@ -177,7 +175,6 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
Some(RequireFunc::Closure(func)) => { Some(RequireFunc::Closure(func)) => {
register_required.push(quote! { register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>( components.register_required_components_manual::<Self, #ident>(
storages,
required_components, required_components,
|| { let x: #ident = (#func)().into(); x }, || { let x: #ident = (#func)().into(); x },
inheritance_depth, inheritance_depth,
@ -188,7 +185,6 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
None => { None => {
register_required.push(quote! { register_required.push(quote! {
components.register_required_components_manual::<Self, #ident>( components.register_required_components_manual::<Self, #ident>(
storages,
required_components, required_components,
<#ident as Default>::default, <#ident as Default>::default,
inheritance_depth, inheritance_depth,
@ -224,13 +220,12 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
fn register_required_components( fn register_required_components(
requiree: #bevy_ecs_path::component::ComponentId, requiree: #bevy_ecs_path::component::ComponentId,
components: &mut #bevy_ecs_path::component::Components, components: &mut #bevy_ecs_path::component::Components,
storages: &mut #bevy_ecs_path::storage::Storages,
required_components: &mut #bevy_ecs_path::component::RequiredComponents, required_components: &mut #bevy_ecs_path::component::RequiredComponents,
inheritance_depth: u16, inheritance_depth: u16,
recursion_check_stack: &mut #bevy_ecs_path::__macro_exports::Vec<#bevy_ecs_path::component::ComponentId> recursion_check_stack: &mut #bevy_ecs_path::__macro_exports::Vec<#bevy_ecs_path::component::ComponentId>
) { ) {
#bevy_ecs_path::component::enforce_no_required_components_recursion(components, recursion_check_stack); #bevy_ecs_path::component::enforce_no_required_components_recursion(components, recursion_check_stack);
let self_id = components.register_component::<Self>(storages); let self_id = components.register_component::<Self>();
recursion_check_stack.push(self_id); recursion_check_stack.push(self_id);
#(#register_required)* #(#register_required)*
#(#register_recursive_requires)* #(#register_recursive_requires)*

View File

@ -86,10 +86,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
match field_kind { match field_kind {
BundleFieldKind::Component => { BundleFieldKind::Component => {
field_component_ids.push(quote! { field_component_ids.push(quote! {
<#field_type as #ecs_path::bundle::Bundle>::component_ids(components, storages, &mut *ids); <#field_type as #ecs_path::bundle::Bundle>::component_ids(components, &mut *ids);
}); });
field_required_components.push(quote! { field_required_components.push(quote! {
<#field_type as #ecs_path::bundle::Bundle>::register_required_components(components, storages, required_components); <#field_type as #ecs_path::bundle::Bundle>::register_required_components(components, required_components);
}); });
field_get_component_ids.push(quote! { field_get_component_ids.push(quote! {
<#field_type as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids); <#field_type as #ecs_path::bundle::Bundle>::get_component_ids(components, &mut *ids);
@ -134,7 +134,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause { unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
fn component_ids( fn component_ids(
components: &mut #ecs_path::component::Components, components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
ids: &mut impl FnMut(#ecs_path::component::ComponentId) ids: &mut impl FnMut(#ecs_path::component::ComponentId)
){ ){
#(#field_component_ids)* #(#field_component_ids)*
@ -159,7 +158,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
fn register_required_components( fn register_required_components(
components: &mut #ecs_path::component::Components, components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
required_components: &mut #ecs_path::component::RequiredComponents required_components: &mut #ecs_path::component::RequiredComponents
){ ){
#(#field_required_components)* #(#field_required_components)*

View File

@ -151,11 +151,7 @@ use variadics_please::all_tuples;
pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static { pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s /// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
#[doc(hidden)] #[doc(hidden)]
fn component_ids( fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId));
components: &mut Components,
storages: &mut Storages,
ids: &mut impl FnMut(ComponentId),
);
/// Gets this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered. /// Gets this [`Bundle`]'s component ids. This will be [`None`] if the component has not been registered.
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)); fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>));
@ -176,7 +172,6 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
/// Registers components that are required by the components in this [`Bundle`]. /// Registers components that are required by the components in this [`Bundle`].
fn register_required_components( fn register_required_components(
_components: &mut Components, _components: &mut Components,
_storages: &mut Storages,
_required_components: &mut RequiredComponents, _required_components: &mut RequiredComponents,
); );
} }
@ -198,12 +193,8 @@ pub trait DynamicBundle {
// - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant. // - `Bundle::get_components` is called exactly once for C and passes the component's storage type based on its associated constant.
// - `Bundle::from_components` calls `func` exactly once for C, which is the exact value returned by `Bundle::component_ids`. // - `Bundle::from_components` calls `func` exactly once for C, which is the exact value returned by `Bundle::component_ids`.
unsafe impl<C: Component> Bundle for C { unsafe impl<C: Component> Bundle for C {
fn component_ids( fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)) {
components: &mut Components, ids(components.register_component::<C>());
storages: &mut Storages,
ids: &mut impl FnMut(ComponentId),
) {
ids(components.register_component::<C>(storages));
} }
unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self unsafe fn from_components<T, F>(ctx: &mut T, func: &mut F) -> Self
@ -219,14 +210,12 @@ unsafe impl<C: Component> Bundle for C {
fn register_required_components( fn register_required_components(
components: &mut Components, components: &mut Components,
storages: &mut Storages,
required_components: &mut RequiredComponents, required_components: &mut RequiredComponents,
) { ) {
let component_id = components.register_component::<C>(storages); let component_id = components.register_component::<C>();
<C as Component>::register_required_components( <C as Component>::register_required_components(
component_id, component_id,
components, components,
storages,
required_components, required_components,
0, 0,
&mut Vec::new(), &mut Vec::new(),
@ -264,8 +253,8 @@ macro_rules! tuple_impl {
// - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct // - `Bundle::get_components` is called exactly once for each member. Relies on the above implementation to pass the correct
// `StorageType` into the callback. // `StorageType` into the callback.
unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) { unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
fn component_ids(components: &mut Components, storages: &mut Storages, ids: &mut impl FnMut(ComponentId)){ fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)){
$(<$name as Bundle>::component_ids(components, storages, ids);)* $(<$name as Bundle>::component_ids(components, ids);)*
} }
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){ fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){
@ -291,10 +280,9 @@ macro_rules! tuple_impl {
fn register_required_components( fn register_required_components(
components: &mut Components, components: &mut Components,
storages: &mut Storages,
required_components: &mut RequiredComponents, required_components: &mut RequiredComponents,
) { ) {
$(<$name as Bundle>::register_required_components(components, storages, required_components);)* $(<$name as Bundle>::register_required_components(components, required_components);)*
} }
} }
@ -392,19 +380,19 @@ impl BundleInfo {
/// ///
/// # Safety /// # Safety
/// ///
/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`, /// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
/// must have its storage initialized (i.e. columns created in tables, sparse set created),
/// and must be in the same order as the source bundle type writes its components in. /// and must be in the same order as the source bundle type writes its components in.
unsafe fn new( unsafe fn new(
bundle_type_name: &'static str, bundle_type_name: &'static str,
storages: &mut Storages,
components: &Components, components: &Components,
mut component_ids: Vec<ComponentId>, mut component_ids: Vec<ComponentId>,
id: BundleId, id: BundleId,
) -> BundleInfo { ) -> BundleInfo {
// check for duplicates
let mut deduped = component_ids.clone(); let mut deduped = component_ids.clone();
deduped.sort_unstable(); deduped.sort_unstable();
deduped.dedup(); deduped.dedup();
if deduped.len() != component_ids.len() { if deduped.len() != component_ids.len() {
// TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized // TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
let mut seen = <HashSet<_>>::default(); let mut seen = <HashSet<_>>::default();
@ -427,18 +415,25 @@ impl BundleInfo {
panic!("Bundle {bundle_type_name} has duplicate components: {names}"); panic!("Bundle {bundle_type_name} has duplicate components: {names}");
} }
// handle explicit components
let explicit_components_len = component_ids.len(); let explicit_components_len = component_ids.len();
let mut required_components = RequiredComponents::default(); let mut required_components = RequiredComponents::default();
for component_id in component_ids.iter().copied() { for component_id in component_ids.iter().copied() {
// SAFETY: caller has verified that all ids are valid // SAFETY: caller has verified that all ids are valid
let info = unsafe { components.get_info_unchecked(component_id) }; let info = unsafe { components.get_info_unchecked(component_id) };
required_components.merge(info.required_components()); required_components.merge(info.required_components());
storages.prepare_component(info);
} }
required_components.remove_explicit_components(&component_ids); required_components.remove_explicit_components(&component_ids);
// handle required components
let required_components = required_components let required_components = required_components
.0 .0
.into_iter() .into_iter()
.map(|(component_id, v)| { .map(|(component_id, v)| {
// Safety: These ids came out of the passed `components`, so they must be valid.
let info = unsafe { components.get_info_unchecked(component_id) };
storages.prepare_component(info);
// This adds required components to the component_ids list _after_ using that list to remove explicitly provided // This adds required components to the component_ids list _after_ using that list to remove explicitly provided
// components. This ordering is important! // components. This ordering is important!
component_ids.push(component_id); component_ids.push(component_id);
@ -550,7 +545,7 @@ impl BundleInfo {
StorageType::Table => { StorageType::Table => {
// SAFETY: bundle_component is a valid index for this bundle // SAFETY: bundle_component is a valid index for this bundle
let status = unsafe { bundle_component_status.get_status(bundle_component) }; let status = unsafe { bundle_component_status.get_status(bundle_component) };
// SAFETY: If component_id is in self.component_ids, BundleInfo::new requires that // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
// the target table contains the component. // the target table contains the component.
let column = table.get_column_mut(component_id).debug_checked_unwrap(); let column = table.get_column_mut(component_id).debug_checked_unwrap();
match (status, insert_mode) { match (status, insert_mode) {
@ -577,7 +572,7 @@ impl BundleInfo {
} }
StorageType::SparseSet => { StorageType::SparseSet => {
let sparse_set = let sparse_set =
// SAFETY: If component_id is in self.component_ids, BundleInfo::new requires that // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
// a sparse set exists for the component. // a sparse set exists for the component.
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
sparse_set.insert( sparse_set.insert(
@ -1540,14 +1535,14 @@ impl Bundles {
let bundle_infos = &mut self.bundle_infos; let bundle_infos = &mut self.bundle_infos;
let id = *self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| { let id = *self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
let mut component_ids= Vec::new(); let mut component_ids= Vec::new();
T::component_ids(components, storages, &mut |id| component_ids.push(id)); T::component_ids(components, &mut |id| component_ids.push(id));
let id = BundleId(bundle_infos.len()); let id = BundleId(bundle_infos.len());
let bundle_info = let bundle_info =
// SAFETY: T::component_id ensures: // SAFETY: T::component_id ensures:
// - its info was created // - its info was created
// - appropriate storage for it has been initialized. // - appropriate storage for it has been initialized.
// - it was created in the same order as the components in T // - it was created in the same order as the components in T
unsafe { BundleInfo::new(core::any::type_name::<T>(), components, component_ids, id) }; unsafe { BundleInfo::new(core::any::type_name::<T>(), storages, components, component_ids, id) };
bundle_infos.push(bundle_info); bundle_infos.push(bundle_info);
id id
}); });
@ -1577,7 +1572,7 @@ impl Bundles {
}; };
// SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as // SAFETY: this is sound because the contributed_components Vec for explicit_bundle_id will not be accessed mutably as
// part of init_dynamic_info. No mutable references will be created and the allocation will remain valid. // part of init_dynamic_info. No mutable references will be created and the allocation will remain valid.
self.init_dynamic_info(components, core::slice::from_raw_parts(ptr, len)) self.init_dynamic_info(storages, components, core::slice::from_raw_parts(ptr, len))
}; };
self.contributed_bundle_ids.insert(TypeId::of::<T>(), id); self.contributed_bundle_ids.insert(TypeId::of::<T>(), id);
id id
@ -1615,6 +1610,7 @@ impl Bundles {
/// provided [`Components`]. /// provided [`Components`].
pub(crate) fn init_dynamic_info( pub(crate) fn init_dynamic_info(
&mut self, &mut self,
storages: &mut Storages,
components: &Components, components: &Components,
component_ids: &[ComponentId], component_ids: &[ComponentId],
) -> BundleId { ) -> BundleId {
@ -1626,8 +1622,12 @@ impl Bundles {
.raw_entry_mut() .raw_entry_mut()
.from_key(component_ids) .from_key(component_ids)
.or_insert_with(|| { .or_insert_with(|| {
let (id, storages) = let (id, storages) = initialize_dynamic_bundle(
initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids)); bundle_infos,
storages,
components,
Vec::from(component_ids),
);
// SAFETY: The ID always increases when new bundles are added, and so, the ID is unique. // SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.
unsafe { unsafe {
self.dynamic_bundle_storages self.dynamic_bundle_storages
@ -1645,6 +1645,7 @@ impl Bundles {
/// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`]. /// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].
pub(crate) fn init_component_info( pub(crate) fn init_component_info(
&mut self, &mut self,
storages: &mut Storages,
components: &Components, components: &Components,
component_id: ComponentId, component_id: ComponentId,
) -> BundleId { ) -> BundleId {
@ -1653,8 +1654,12 @@ impl Bundles {
.dynamic_component_bundle_ids .dynamic_component_bundle_ids
.entry(component_id) .entry(component_id)
.or_insert_with(|| { .or_insert_with(|| {
let (id, storage_type) = let (id, storage_type) = initialize_dynamic_bundle(
initialize_dynamic_bundle(bundle_infos, components, vec![component_id]); bundle_infos,
storages,
components,
vec![component_id],
);
self.dynamic_component_storages.insert(id, storage_type[0]); self.dynamic_component_storages.insert(id, storage_type[0]);
id id
}); });
@ -1666,6 +1671,7 @@ impl Bundles {
/// and initializes a [`BundleInfo`]. /// and initializes a [`BundleInfo`].
fn initialize_dynamic_bundle( fn initialize_dynamic_bundle(
bundle_infos: &mut Vec<BundleInfo>, bundle_infos: &mut Vec<BundleInfo>,
storages: &mut Storages,
components: &Components, components: &Components,
component_ids: Vec<ComponentId>, component_ids: Vec<ComponentId>,
) -> (BundleId, Vec<StorageType>) { ) -> (BundleId, Vec<StorageType>) {
@ -1681,7 +1687,7 @@ fn initialize_dynamic_bundle(
let id = BundleId(bundle_infos.len()); let id = BundleId(bundle_infos.len());
let bundle_info = let bundle_info =
// SAFETY: `component_ids` are valid as they were just checked // SAFETY: `component_ids` are valid as they were just checked
unsafe { BundleInfo::new("<dynamic bundle>", components, component_ids, id) }; unsafe { BundleInfo::new("<dynamic bundle>", storages, components, component_ids, id) };
bundle_infos.push(bundle_info); bundle_infos.push(bundle_info);
(id, storage_types) (id, storage_types)

View File

@ -8,7 +8,7 @@ use crate::{
entity::{ComponentCloneCtx, Entity}, entity::{ComponentCloneCtx, Entity},
query::DebugCheckedUnwrap, query::DebugCheckedUnwrap,
resource::Resource, resource::Resource,
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow}, storage::{SparseSetIndex, SparseSets, Table, TableRow},
system::{Local, SystemParam}, system::{Local, SystemParam},
world::{DeferredWorld, FromWorld, World}, world::{DeferredWorld, FromWorld, World},
}; };
@ -438,7 +438,6 @@ pub trait Component: Send + Sync + 'static {
fn register_required_components( fn register_required_components(
_component_id: ComponentId, _component_id: ComponentId,
_components: &mut Components, _components: &mut Components,
_storages: &mut Storages,
_required_components: &mut RequiredComponents, _required_components: &mut RequiredComponents,
_inheritance_depth: u16, _inheritance_depth: u16,
_recursion_check_stack: &mut Vec<ComponentId>, _recursion_check_stack: &mut Vec<ComponentId>,
@ -1193,14 +1192,13 @@ impl Components {
/// * [`Components::component_id()`] /// * [`Components::component_id()`]
/// * [`Components::register_component_with_descriptor()`] /// * [`Components::register_component_with_descriptor()`]
#[inline] #[inline]
pub fn register_component<T: Component>(&mut self, storages: &mut Storages) -> ComponentId { pub fn register_component<T: Component>(&mut self) -> ComponentId {
self.register_component_internal::<T>(storages, &mut Vec::new()) self.register_component_internal::<T>(&mut Vec::new())
} }
#[inline] #[inline]
fn register_component_internal<T: Component>( fn register_component_internal<T: Component>(
&mut self, &mut self,
storages: &mut Storages,
recursion_check_stack: &mut Vec<ComponentId>, recursion_check_stack: &mut Vec<ComponentId>,
) -> ComponentId { ) -> ComponentId {
let mut is_new_registration = false; let mut is_new_registration = false;
@ -1214,7 +1212,6 @@ impl Components {
*indices.entry(type_id).or_insert_with(|| { *indices.entry(type_id).or_insert_with(|| {
let id = Components::register_component_inner( let id = Components::register_component_inner(
components, components,
storages,
ComponentDescriptor::new::<T>(), ComponentDescriptor::new::<T>(),
); );
is_new_registration = true; is_new_registration = true;
@ -1226,7 +1223,6 @@ impl Components {
T::register_required_components( T::register_required_components(
id, id,
self, self,
storages,
&mut required_components, &mut required_components,
0, 0,
recursion_check_stack, recursion_check_stack,
@ -1261,23 +1257,18 @@ impl Components {
/// * [`Components::register_component()`] /// * [`Components::register_component()`]
pub fn register_component_with_descriptor( pub fn register_component_with_descriptor(
&mut self, &mut self,
storages: &mut Storages,
descriptor: ComponentDescriptor, descriptor: ComponentDescriptor,
) -> ComponentId { ) -> ComponentId {
Components::register_component_inner(&mut self.components, storages, descriptor) Components::register_component_inner(&mut self.components, descriptor)
} }
#[inline] #[inline]
fn register_component_inner( fn register_component_inner(
components: &mut Vec<ComponentInfo>, components: &mut Vec<ComponentInfo>,
storages: &mut Storages,
descriptor: ComponentDescriptor, descriptor: ComponentDescriptor,
) -> ComponentId { ) -> ComponentId {
let component_id = ComponentId(components.len()); let component_id = ComponentId(components.len());
let info = ComponentInfo::new(component_id, descriptor); let info = ComponentInfo::new(component_id, descriptor);
if info.descriptor.storage_type == StorageType::SparseSet {
storages.sparse_sets.get_or_insert(&info);
}
components.push(info); components.push(info);
component_id component_id
} }
@ -1512,14 +1503,13 @@ impl Components {
#[doc(hidden)] #[doc(hidden)]
pub fn register_required_components_manual<T: Component, R: Component>( pub fn register_required_components_manual<T: Component, R: Component>(
&mut self, &mut self,
storages: &mut Storages,
required_components: &mut RequiredComponents, required_components: &mut RequiredComponents,
constructor: fn() -> R, constructor: fn() -> R,
inheritance_depth: u16, inheritance_depth: u16,
recursion_check_stack: &mut Vec<ComponentId>, recursion_check_stack: &mut Vec<ComponentId>,
) { ) {
let requiree = self.register_component_internal::<T>(storages, recursion_check_stack); let requiree = self.register_component_internal::<T>(recursion_check_stack);
let required = self.register_component_internal::<R>(storages, recursion_check_stack); let required = self.register_component_internal::<R>(recursion_check_stack);
// SAFETY: We just created the components. // SAFETY: We just created the components.
unsafe { unsafe {
@ -2111,11 +2101,10 @@ impl RequiredComponents {
pub fn register<C: Component>( pub fn register<C: Component>(
&mut self, &mut self,
components: &mut Components, components: &mut Components,
storages: &mut Storages,
constructor: fn() -> C, constructor: fn() -> C,
inheritance_depth: u16, inheritance_depth: u16,
) { ) {
let component_id = components.register_component::<C>(storages); let component_id = components.register_component::<C>();
self.register_by_id(component_id, constructor, inheritance_depth); self.register_by_id(component_id, constructor, inheritance_depth);
} }

View File

@ -229,13 +229,9 @@ mod tests {
y: SparseStored, y: SparseStored,
} }
let mut ids = Vec::new(); let mut ids = Vec::new();
<FooBundle as Bundle>::component_ids( <FooBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
&mut world.components, ids.push(id);
&mut world.storages, });
&mut |id| {
ids.push(id);
},
);
assert_eq!( assert_eq!(
ids, ids,
@ -283,13 +279,9 @@ mod tests {
} }
let mut ids = Vec::new(); let mut ids = Vec::new();
<NestedBundle as Bundle>::component_ids( <NestedBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
&mut world.components, ids.push(id);
&mut world.storages, });
&mut |id| {
ids.push(id);
},
);
assert_eq!( assert_eq!(
ids, ids,
@ -339,13 +331,9 @@ mod tests {
} }
let mut ids = Vec::new(); let mut ids = Vec::new();
<BundleWithIgnored as Bundle>::component_ids( <BundleWithIgnored as Bundle>::component_ids(&mut world.components, &mut |id| {
&mut world.components, ids.push(id);
&mut world.storages, });
&mut |id| {
ids.push(id);
},
);
assert_eq!(ids, &[world.register_component::<C>(),]); assert_eq!(ids, &[world.register_component::<C>(),]);

View File

@ -407,7 +407,7 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
world.commands().queue(move |world: &mut World| { world.commands().queue(move |world: &mut World| {
let event_id = E::register_component_id(world); let event_id = E::register_component_id(world);
let mut components = Vec::new(); let mut components = Vec::new();
B::component_ids(&mut world.components, &mut world.storages, &mut |id| { B::component_ids(&mut world.components, &mut |id| {
components.push(id); components.push(id);
}); });
let mut descriptor = ObserverDescriptor { let mut descriptor = ObserverDescriptor {

View File

@ -1091,7 +1091,7 @@ pub struct ReadFetch<'w, T: Component> {
// T::STORAGE_TYPE = StorageType::Table // T::STORAGE_TYPE = StorageType::Table
Option<ThinSlicePtr<'w, UnsafeCell<T>>>, Option<ThinSlicePtr<'w, UnsafeCell<T>>>,
// T::STORAGE_TYPE = StorageType::SparseSet // T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet, Option<&'w ComponentSparseSet>,
>, >,
} }
@ -1130,13 +1130,7 @@ unsafe impl<T: Component> WorldQuery for &T {
// which we are allowed to access since we registered it in `update_archetype_component_access`. // which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared // Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`. // reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe { unsafe { world.storages().sparse_sets.get(component_id) }
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}, },
), ),
} }
@ -1233,7 +1227,12 @@ unsafe impl<T: Component> QueryData for &T {
}, },
|sparse_set| { |sparse_set| {
// SAFETY: Caller ensures `entity` is in range. // SAFETY: Caller ensures `entity` is in range.
let item = unsafe { sparse_set.get(entity).debug_checked_unwrap() }; let item = unsafe {
sparse_set
.debug_checked_unwrap()
.get(entity)
.debug_checked_unwrap()
};
item.deref() item.deref()
}, },
) )
@ -1255,7 +1254,8 @@ pub struct RefFetch<'w, T: Component> {
MaybeThinSlicePtrLocation<'w>, MaybeThinSlicePtrLocation<'w>,
)>, )>,
// T::STORAGE_TYPE = StorageType::SparseSet // T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet, // Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>, >,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
@ -1296,13 +1296,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
// which we are allowed to access since we registered it in `update_archetype_component_access`. // which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared // Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`. // reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe { unsafe { world.storages().sparse_sets.get(component_id) }
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}, },
), ),
last_run, last_run,
@ -1424,9 +1418,13 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> {
} }
}, },
|sparse_set| { |sparse_set| {
// SAFETY: The caller ensures `entity` is in range. // SAFETY: The caller ensures `entity` is in range and has the component.
let (component, ticks, _caller) = let (component, ticks, _caller) = unsafe {
unsafe { sparse_set.get_with_ticks(entity).debug_checked_unwrap() }; sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Ref { Ref {
value: component.deref(), value: component.deref(),
@ -1454,7 +1452,8 @@ pub struct WriteFetch<'w, T: Component> {
MaybeThinSlicePtrLocation<'w>, MaybeThinSlicePtrLocation<'w>,
)>, )>,
// T::STORAGE_TYPE = StorageType::SparseSet // T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet, // Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>, >,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
@ -1495,13 +1494,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
// which we are allowed to access since we registered it in `update_archetype_component_access`. // which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared // Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`. // reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe { unsafe { world.storages().sparse_sets.get(component_id) }
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}, },
), ),
last_run, last_run,
@ -1623,9 +1616,13 @@ unsafe impl<'__w, T: Component<Mutability = Mutable>> QueryData for &'__w mut T
} }
}, },
|sparse_set| { |sparse_set| {
// SAFETY: The caller ensures `entity` is in range. // SAFETY: The caller ensures `entity` is in range and has the component.
let (component, ticks, _caller) = let (component, ticks, _caller) = unsafe {
unsafe { sparse_set.get_with_ticks(entity).debug_checked_unwrap() }; sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Mut { Mut {
value: component.assert_unique().deref_mut(), value: component.assert_unique().deref_mut(),

View File

@ -630,7 +630,8 @@ pub struct AddedFetch<'w, T: Component> {
// T::STORAGE_TYPE = StorageType::Table // T::STORAGE_TYPE = StorageType::Table
Option<ThinSlicePtr<'w, UnsafeCell<Tick>>>, Option<ThinSlicePtr<'w, UnsafeCell<Tick>>>,
// T::STORAGE_TYPE = StorageType::SparseSet // T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet, // Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>, >,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
@ -674,7 +675,7 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
// which we are allowed to access since we registered it in `update_archetype_component_access`. // which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components' ticks in this function, we just get a shared // Note that we do not actually access any components' ticks in this function, we just get a shared
// reference to the sparse set, which is used to access the components' ticks in `Self::fetch`. // reference to the sparse set, which is used to access the components' ticks in `Self::fetch`.
unsafe { world.storages().sparse_sets.get(id).debug_checked_unwrap() } unsafe { world.storages().sparse_sets.get(id) }
}, },
), ),
last_run, last_run,
@ -766,7 +767,10 @@ unsafe impl<T: Component> QueryFilter for Added<T> {
|sparse_set| { |sparse_set| {
// SAFETY: The caller ensures `entity` is in range. // SAFETY: The caller ensures `entity` is in range.
let tick = unsafe { let tick = unsafe {
ComponentSparseSet::get_added_tick(sparse_set, entity).debug_checked_unwrap() sparse_set
.debug_checked_unwrap()
.get_added_tick(entity)
.debug_checked_unwrap()
}; };
tick.deref().is_newer_than(fetch.last_run, fetch.this_run) tick.deref().is_newer_than(fetch.last_run, fetch.this_run)
@ -850,7 +854,12 @@ pub struct Changed<T>(PhantomData<T>);
#[doc(hidden)] #[doc(hidden)]
pub struct ChangedFetch<'w, T: Component> { pub struct ChangedFetch<'w, T: Component> {
ticks: StorageSwitch<T, Option<ThinSlicePtr<'w, UnsafeCell<Tick>>>, &'w ComponentSparseSet>, ticks: StorageSwitch<
T,
Option<ThinSlicePtr<'w, UnsafeCell<Tick>>>,
// Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>,
last_run: Tick, last_run: Tick,
this_run: Tick, this_run: Tick,
} }
@ -893,7 +902,7 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
// which we are allowed to access since we registered it in `update_archetype_component_access`. // which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components' ticks in this function, we just get a shared // Note that we do not actually access any components' ticks in this function, we just get a shared
// reference to the sparse set, which is used to access the components' ticks in `Self::fetch`. // reference to the sparse set, which is used to access the components' ticks in `Self::fetch`.
unsafe { world.storages().sparse_sets.get(id).debug_checked_unwrap() } unsafe { world.storages().sparse_sets.get(id) }
}, },
), ),
last_run, last_run,
@ -986,7 +995,10 @@ unsafe impl<T: Component> QueryFilter for Changed<T> {
|sparse_set| { |sparse_set| {
// SAFETY: The caller ensures `entity` is in range. // SAFETY: The caller ensures `entity` is in range.
let tick = unsafe { let tick = unsafe {
ComponentSparseSet::get_changed_tick(sparse_set, entity).debug_checked_unwrap() sparse_set
.debug_checked_unwrap()
.get_changed_tick(entity)
.debug_checked_unwrap()
}; };
tick.deref().is_newer_than(fetch.last_run, fetch.this_run) tick.deref().is_newer_than(fetch.last_run, fetch.this_run)

View File

@ -31,10 +31,13 @@ pub use resource::*;
pub use sparse_set::*; pub use sparse_set::*;
pub use table::*; pub use table::*;
use crate::component::{ComponentInfo, StorageType};
/// The raw data stores of a [`World`](crate::world::World) /// The raw data stores of a [`World`](crate::world::World)
#[derive(Default)] #[derive(Default)]
pub struct Storages { pub struct Storages {
/// Backing storage for [`SparseSet`] components. /// Backing storage for [`SparseSet`] components.
/// Note that sparse sets are only present for components that have been spawned or have had a relevant bundle registered.
pub sparse_sets: SparseSets, pub sparse_sets: SparseSets,
/// Backing storage for [`Table`] components. /// Backing storage for [`Table`] components.
pub tables: Tables, pub tables: Tables,
@ -43,3 +46,17 @@ pub struct Storages {
/// Backing storage for `!Send` resources. /// Backing storage for `!Send` resources.
pub non_send_resources: Resources<false>, pub non_send_resources: Resources<false>,
} }
impl Storages {
/// ensures that the component has its necessary storage initialize.
pub fn prepare_component(&mut self, component: &ComponentInfo) {
match component.storage_type() {
StorageType::Table => {
// table needs no preparation
}
StorageType::SparseSet => {
self.sparse_sets.get_or_insert(component);
}
}
}
}

View File

@ -616,7 +616,7 @@ impl SparseSets {
self.sets.iter().map(|(id, data)| (*id, data)) self.sets.iter().map(|(id, data)| (*id, data))
} }
/// Gets a reference to the [`ComponentSparseSet`] of a [`ComponentId`]. /// Gets a reference to the [`ComponentSparseSet`] of a [`ComponentId`]. This may be `None` if the component has never been spawned.
#[inline] #[inline]
pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> { pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.sets.get(component_id) self.sets.get(component_id)
@ -638,7 +638,7 @@ impl SparseSets {
self.sets.get_mut(component_info.id()).unwrap() self.sets.get_mut(component_info.id()).unwrap()
} }
/// Gets a mutable reference to the [`ComponentSparseSet`] of a [`ComponentId`]. /// Gets a mutable reference to the [`ComponentSparseSet`] of a [`ComponentId`]. This may be `None` if the component has never been spawned.
pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> { pub(crate) fn get_mut(&mut self, component_id: ComponentId) -> Option<&mut ComponentSparseSet> {
self.sets.get_mut(component_id) self.sets.get_mut(component_id)
} }

View File

@ -820,7 +820,7 @@ mod tests {
component::{Component, Components, Tick}, component::{Component, Components, Tick},
entity::Entity, entity::Entity,
ptr::OwningPtr, ptr::OwningPtr,
storage::{Storages, TableBuilder, TableId, TableRow, Tables}, storage::{TableBuilder, TableId, TableRow, Tables},
}; };
use alloc::vec::Vec; use alloc::vec::Vec;
@ -845,8 +845,7 @@ mod tests {
#[test] #[test]
fn table() { fn table() {
let mut components = Components::default(); let mut components = Components::default();
let mut storages = Storages::default(); let component_id = components.register_component::<W<TableRow>>();
let component_id = components.register_component::<W<TableRow>>(&mut storages);
let columns = &[component_id]; let columns = &[component_id];
let mut table = TableBuilder::with_capacity(0, columns.len()) let mut table = TableBuilder::with_capacity(0, columns.len())
.add_column(components.get_info(component_id).unwrap()) .add_column(components.get_info(component_id).unwrap())

View File

@ -1614,10 +1614,11 @@ impl<'w> EntityWorldMut<'w> {
) -> &mut Self { ) -> &mut Self {
self.assert_not_despawned(); self.assert_not_despawned();
let change_tick = self.world.change_tick(); let change_tick = self.world.change_tick();
let bundle_id = self let bundle_id = self.world.bundles.init_component_info(
.world &mut self.world.storages,
.bundles &self.world.components,
.init_component_info(&self.world.components, component_id); component_id,
);
let storage_type = self.world.bundles.get_storage_unchecked(bundle_id); let storage_type = self.world.bundles.get_storage_unchecked(bundle_id);
let bundle_inserter = BundleInserter::new_with_id( let bundle_inserter = BundleInserter::new_with_id(
@ -1665,10 +1666,11 @@ impl<'w> EntityWorldMut<'w> {
) -> &mut Self { ) -> &mut Self {
self.assert_not_despawned(); self.assert_not_despawned();
let change_tick = self.world.change_tick(); let change_tick = self.world.change_tick();
let bundle_id = self let bundle_id = self.world.bundles.init_dynamic_info(
.world &mut self.world.storages,
.bundles &self.world.components,
.init_dynamic_info(&self.world.components, component_ids); component_ids,
);
let mut storage_types = let mut storage_types =
core::mem::take(self.world.bundles.get_storages_unchecked(bundle_id)); core::mem::take(self.world.bundles.get_storages_unchecked(bundle_id));
let bundle_inserter = BundleInserter::new_with_id( let bundle_inserter = BundleInserter::new_with_id(
@ -1771,6 +1773,7 @@ impl<'w> EntityWorldMut<'w> {
// - entity location is valid // - entity location is valid
// - table row is removed below, without dropping the contents // - table row is removed below, without dropping the contents
// - `components` comes from the same world as `storages` // - `components` comes from the same world as `storages`
// - the component exists on the entity
take_component( take_component(
storages, storages,
components, components,
@ -1955,6 +1958,7 @@ impl<'w> EntityWorldMut<'w> {
.storages .storages
.sparse_sets .sparse_sets
.get_mut(component_id) .get_mut(component_id)
// Set exists because the component existed on the entity
.unwrap() .unwrap()
.remove(entity); .remove(entity);
} }
@ -2090,7 +2094,10 @@ impl<'w> EntityWorldMut<'w> {
.components() .components()
.filter(|c| !retained_bundle_info.contributed_components().contains(c)) .filter(|c| !retained_bundle_info.contributed_components().contains(c))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let remove_bundle = self.world.bundles.init_dynamic_info(components, to_remove); let remove_bundle =
self.world
.bundles
.init_dynamic_info(&mut self.world.storages, components, to_remove);
// SAFETY: the `BundleInfo` for the components to remove is initialized above // SAFETY: the `BundleInfo` for the components to remove is initialized above
self.location = unsafe { self.location = unsafe {
@ -2131,10 +2138,11 @@ impl<'w> EntityWorldMut<'w> {
self.assert_not_despawned(); self.assert_not_despawned();
let components = &mut self.world.components; let components = &mut self.world.components;
let bundle_id = self let bundle_id = self.world.bundles.init_component_info(
.world &mut self.world.storages,
.bundles components,
.init_component_info(components, component_id); component_id,
);
// SAFETY: the `BundleInfo` for this `component_id` is initialized above // SAFETY: the `BundleInfo` for this `component_id` is initialized above
self.location = unsafe { self.location = unsafe {
@ -2162,10 +2170,11 @@ impl<'w> EntityWorldMut<'w> {
self.assert_not_despawned(); self.assert_not_despawned();
let components = &mut self.world.components; let components = &mut self.world.components;
let bundle_id = self let bundle_id = self.world.bundles.init_dynamic_info(
.world &mut self.world.storages,
.bundles components,
.init_dynamic_info(components, component_ids); component_ids,
);
// SAFETY: the `BundleInfo` for this `bundle_id` is initialized above // SAFETY: the `BundleInfo` for this `bundle_id` is initialized above
unsafe { unsafe {
@ -2203,10 +2212,11 @@ impl<'w> EntityWorldMut<'w> {
let component_ids: Vec<ComponentId> = self.archetype().components().collect(); let component_ids: Vec<ComponentId> = self.archetype().components().collect();
let components = &mut self.world.components; let components = &mut self.world.components;
let bundle_id = self let bundle_id = self.world.bundles.init_dynamic_info(
.world &mut self.world.storages,
.bundles components,
.init_dynamic_info(components, component_ids.as_slice()); component_ids.as_slice(),
);
// SAFETY: the `BundleInfo` for this `component_id` is initialized above // SAFETY: the `BundleInfo` for this `component_id` is initialized above
self.location = unsafe { self.location = unsafe {
@ -2354,6 +2364,7 @@ impl<'w> EntityWorldMut<'w> {
table_row = remove_result.table_row; table_row = remove_result.table_row;
for component_id in archetype.sparse_set_components() { for component_id in archetype.sparse_set_components() {
// set must have existed for the component to be added.
let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap(); let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap();
sparse_set.remove(self.entity); sparse_set.remove(self.entity);
} }
@ -4113,6 +4124,9 @@ unsafe fn insert_dynamic_bundle<
/// - `component_id` must be valid /// - `component_id` must be valid
/// - `components` must come from the same world as `self` /// - `components` must come from the same world as `self`
/// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value /// - The relevant table row **must be removed** by the caller once all components are taken, without dropping the value
///
/// # Panics
/// Panics if the entity did not have the component.
#[inline] #[inline]
pub(crate) unsafe fn take_component<'a>( pub(crate) unsafe fn take_component<'a>(
storages: &'a mut Storages, storages: &'a mut Storages,

View File

@ -251,7 +251,7 @@ impl World {
/// Registers a new [`Component`] type and returns the [`ComponentId`] created for it. /// Registers a new [`Component`] type and returns the [`ComponentId`] created for it.
pub fn register_component<T: Component>(&mut self) -> ComponentId { pub fn register_component<T: Component>(&mut self) -> ComponentId {
self.components.register_component::<T>(&mut self.storages) self.components.register_component::<T>()
} }
/// Returns a mutable reference to the [`ComponentHooks`] for a [`Component`] type. /// Returns a mutable reference to the [`ComponentHooks`] for a [`Component`] type.
@ -528,7 +528,7 @@ impl World {
descriptor: ComponentDescriptor, descriptor: ComponentDescriptor,
) -> ComponentId { ) -> ComponentId {
self.components self.components
.register_component_with_descriptor(&mut self.storages, descriptor) .register_component_with_descriptor(descriptor)
} }
/// Returns the [`ComponentId`] of the given [`Component`] type `T`. /// Returns the [`ComponentId`] of the given [`Component`] type `T`.