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

View File

@ -86,10 +86,10 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
match field_kind {
BundleFieldKind::Component => {
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_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_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 {
fn component_ids(
components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
ids: &mut impl FnMut(#ecs_path::component::ComponentId)
){
#(#field_component_ids)*
@ -159,7 +158,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
fn register_required_components(
components: &mut #ecs_path::component::Components,
storages: &mut #ecs_path::storage::Storages,
required_components: &mut #ecs_path::component::RequiredComponents
){
#(#field_required_components)*

View File

@ -151,11 +151,7 @@ use variadics_please::all_tuples;
pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
/// Gets this [`Bundle`]'s component ids, in the order of this bundle's [`Component`]s
#[doc(hidden)]
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));
/// 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>));
@ -176,7 +172,6 @@ pub unsafe trait Bundle: DynamicBundle + Send + Sync + 'static {
/// Registers components that are required by the components in this [`Bundle`].
fn register_required_components(
_components: &mut Components,
_storages: &mut Storages,
_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::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 {
fn component_ids(
components: &mut Components,
storages: &mut Storages,
ids: &mut impl FnMut(ComponentId),
) {
ids(components.register_component::<C>(storages));
fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)) {
ids(components.register_component::<C>());
}
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(
components: &mut Components,
storages: &mut Storages,
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(
component_id,
components,
storages,
required_components,
0,
&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
// `StorageType` into the callback.
unsafe impl<$($name: Bundle),*> Bundle for ($($name,)*) {
fn component_ids(components: &mut Components, storages: &mut Storages, ids: &mut impl FnMut(ComponentId)){
$(<$name as Bundle>::component_ids(components, storages, ids);)*
fn component_ids(components: &mut Components, ids: &mut impl FnMut(ComponentId)){
$(<$name as Bundle>::component_ids(components, ids);)*
}
fn get_component_ids(components: &Components, ids: &mut impl FnMut(Option<ComponentId>)){
@ -291,10 +280,9 @@ macro_rules! tuple_impl {
fn register_required_components(
components: &mut Components,
storages: &mut Storages,
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
///
/// 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),
/// Every ID in `component_ids` must be valid within the World that owns the `BundleInfo`
/// and must be in the same order as the source bundle type writes its components in.
unsafe fn new(
bundle_type_name: &'static str,
storages: &mut Storages,
components: &Components,
mut component_ids: Vec<ComponentId>,
id: BundleId,
) -> BundleInfo {
// check for duplicates
let mut deduped = component_ids.clone();
deduped.sort_unstable();
deduped.dedup();
if deduped.len() != component_ids.len() {
// TODO: Replace with `Vec::partition_dedup` once https://github.com/rust-lang/rust/issues/54279 is stabilized
let mut seen = <HashSet<_>>::default();
@ -427,18 +415,25 @@ impl BundleInfo {
panic!("Bundle {bundle_type_name} has duplicate components: {names}");
}
// handle explicit components
let explicit_components_len = component_ids.len();
let mut required_components = RequiredComponents::default();
for component_id in component_ids.iter().copied() {
// SAFETY: caller has verified that all ids are valid
let info = unsafe { components.get_info_unchecked(component_id) };
required_components.merge(info.required_components());
storages.prepare_component(info);
}
required_components.remove_explicit_components(&component_ids);
// handle required components
let required_components = required_components
.0
.into_iter()
.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
// components. This ordering is important!
component_ids.push(component_id);
@ -550,7 +545,7 @@ impl BundleInfo {
StorageType::Table => {
// SAFETY: bundle_component is a valid index for this bundle
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.
let column = table.get_column_mut(component_id).debug_checked_unwrap();
match (status, insert_mode) {
@ -577,7 +572,7 @@ impl BundleInfo {
}
StorageType::SparseSet => {
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.
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
sparse_set.insert(
@ -1540,14 +1535,14 @@ impl Bundles {
let bundle_infos = &mut self.bundle_infos;
let id = *self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
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 bundle_info =
// SAFETY: T::component_id ensures:
// - its info was created
// - appropriate storage for it has been initialized.
// - 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);
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
// 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);
id
@ -1615,6 +1610,7 @@ impl Bundles {
/// provided [`Components`].
pub(crate) fn init_dynamic_info(
&mut self,
storages: &mut Storages,
components: &Components,
component_ids: &[ComponentId],
) -> BundleId {
@ -1626,8 +1622,12 @@ impl Bundles {
.raw_entry_mut()
.from_key(component_ids)
.or_insert_with(|| {
let (id, storages) =
initialize_dynamic_bundle(bundle_infos, components, Vec::from(component_ids));
let (id, storages) = initialize_dynamic_bundle(
bundle_infos,
storages,
components,
Vec::from(component_ids),
);
// SAFETY: The ID always increases when new bundles are added, and so, the ID is unique.
unsafe {
self.dynamic_bundle_storages
@ -1645,6 +1645,7 @@ impl Bundles {
/// Panics if the provided [`ComponentId`] does not exist in the provided [`Components`].
pub(crate) fn init_component_info(
&mut self,
storages: &mut Storages,
components: &Components,
component_id: ComponentId,
) -> BundleId {
@ -1653,8 +1654,12 @@ impl Bundles {
.dynamic_component_bundle_ids
.entry(component_id)
.or_insert_with(|| {
let (id, storage_type) =
initialize_dynamic_bundle(bundle_infos, components, vec![component_id]);
let (id, storage_type) = initialize_dynamic_bundle(
bundle_infos,
storages,
components,
vec![component_id],
);
self.dynamic_component_storages.insert(id, storage_type[0]);
id
});
@ -1666,6 +1671,7 @@ impl Bundles {
/// and initializes a [`BundleInfo`].
fn initialize_dynamic_bundle(
bundle_infos: &mut Vec<BundleInfo>,
storages: &mut Storages,
components: &Components,
component_ids: Vec<ComponentId>,
) -> (BundleId, Vec<StorageType>) {
@ -1681,7 +1687,7 @@ fn initialize_dynamic_bundle(
let id = BundleId(bundle_infos.len());
let bundle_info =
// 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);
(id, storage_types)

View File

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

View File

@ -229,13 +229,9 @@ mod tests {
y: SparseStored,
}
let mut ids = Vec::new();
<FooBundle as Bundle>::component_ids(
&mut world.components,
&mut world.storages,
&mut |id| {
ids.push(id);
},
);
<FooBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
ids.push(id);
});
assert_eq!(
ids,
@ -283,13 +279,9 @@ mod tests {
}
let mut ids = Vec::new();
<NestedBundle as Bundle>::component_ids(
&mut world.components,
&mut world.storages,
&mut |id| {
ids.push(id);
},
);
<NestedBundle as Bundle>::component_ids(&mut world.components, &mut |id| {
ids.push(id);
});
assert_eq!(
ids,
@ -339,13 +331,9 @@ mod tests {
}
let mut ids = Vec::new();
<BundleWithIgnored as Bundle>::component_ids(
&mut world.components,
&mut world.storages,
&mut |id| {
ids.push(id);
},
);
<BundleWithIgnored as Bundle>::component_ids(&mut world.components, &mut |id| {
ids.push(id);
});
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| {
let event_id = E::register_component_id(world);
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);
});
let mut descriptor = ObserverDescriptor {

View File

@ -1091,7 +1091,7 @@ pub struct ReadFetch<'w, T: Component> {
// T::STORAGE_TYPE = StorageType::Table
Option<ThinSlicePtr<'w, UnsafeCell<T>>>,
// 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`.
// 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`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
unsafe { world.storages().sparse_sets.get(component_id) }
},
),
}
@ -1233,7 +1227,12 @@ unsafe impl<T: Component> QueryData for &T {
},
|sparse_set| {
// 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()
},
)
@ -1255,7 +1254,8 @@ pub struct RefFetch<'w, T: Component> {
MaybeThinSlicePtrLocation<'w>,
)>,
// T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet,
// Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>,
last_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`.
// 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`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
unsafe { world.storages().sparse_sets.get(component_id) }
},
),
last_run,
@ -1424,9 +1418,13 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> {
}
},
|sparse_set| {
// SAFETY: The caller ensures `entity` is in range.
let (component, ticks, _caller) =
unsafe { sparse_set.get_with_ticks(entity).debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range and has the component.
let (component, ticks, _caller) = unsafe {
sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Ref {
value: component.deref(),
@ -1454,7 +1452,8 @@ pub struct WriteFetch<'w, T: Component> {
MaybeThinSlicePtrLocation<'w>,
)>,
// T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet,
// Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>,
last_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`.
// 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`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
unsafe { world.storages().sparse_sets.get(component_id) }
},
),
last_run,
@ -1623,9 +1616,13 @@ unsafe impl<'__w, T: Component<Mutability = Mutable>> QueryData for &'__w mut T
}
},
|sparse_set| {
// SAFETY: The caller ensures `entity` is in range.
let (component, ticks, _caller) =
unsafe { sparse_set.get_with_ticks(entity).debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range and has the component.
let (component, ticks, _caller) = unsafe {
sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Mut {
value: component.assert_unique().deref_mut(),

View File

@ -630,7 +630,8 @@ pub struct AddedFetch<'w, T: Component> {
// T::STORAGE_TYPE = StorageType::Table
Option<ThinSlicePtr<'w, UnsafeCell<Tick>>>,
// T::STORAGE_TYPE = StorageType::SparseSet
&'w ComponentSparseSet,
// Can be `None` when the component has never been inserted
Option<&'w ComponentSparseSet>,
>,
last_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`.
// 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`.
unsafe { world.storages().sparse_sets.get(id).debug_checked_unwrap() }
unsafe { world.storages().sparse_sets.get(id) }
},
),
last_run,
@ -766,7 +767,10 @@ unsafe impl<T: Component> QueryFilter for Added<T> {
|sparse_set| {
// SAFETY: The caller ensures `entity` is in range.
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)
@ -850,7 +854,12 @@ pub struct Changed<T>(PhantomData<T>);
#[doc(hidden)]
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,
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`.
// 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`.
unsafe { world.storages().sparse_sets.get(id).debug_checked_unwrap() }
unsafe { world.storages().sparse_sets.get(id) }
},
),
last_run,
@ -986,7 +995,10 @@ unsafe impl<T: Component> QueryFilter for Changed<T> {
|sparse_set| {
// SAFETY: The caller ensures `entity` is in range.
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)

View File

@ -31,10 +31,13 @@ pub use resource::*;
pub use sparse_set::*;
pub use table::*;
use crate::component::{ComponentInfo, StorageType};
/// The raw data stores of a [`World`](crate::world::World)
#[derive(Default)]
pub struct Storages {
/// 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,
/// Backing storage for [`Table`] components.
pub tables: Tables,
@ -43,3 +46,17 @@ pub struct Storages {
/// Backing storage for `!Send` resources.
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))
}
/// 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]
pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
self.sets.get(component_id)
@ -638,7 +638,7 @@ impl SparseSets {
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> {
self.sets.get_mut(component_id)
}

View File

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

View File

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

View File

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