Skip to content

Remove ArchetypeComponentId and archetype_component_access #19143

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion benches/benches/bevy_ecs/iteration/heavy_compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ pub fn heavy_compute(c: &mut Criterion) {

let mut system = IntoSystem::into_system(sys);
system.initialize(&mut world);
system.update_archetype_component_access(world.as_unsafe_world_cell());

b.iter(move || system.run((), &mut world));
});
Expand Down
1 change: 0 additions & 1 deletion benches/benches/bevy_ecs/iteration/iter_simple_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ impl Benchmark {

let mut system = IntoSystem::into_system(query_system);
system.initialize(&mut world);
system.update_archetype_component_access(world.as_unsafe_world_cell());
Self(world, Box::new(system))
}

Expand Down
7 changes: 1 addition & 6 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,6 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}
}

unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta) }
}

fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
<#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::apply(&mut state.state, system_meta, world);
}
Expand All @@ -447,7 +442,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {

#[inline]
unsafe fn validate_param<'w, 's>(
state: &'s Self::State,
state: &'s mut Self::State,
_system_meta: &#path::system::SystemMeta,
_world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
) -> Result<(), #path::system::SystemParamValidationError> {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/macros/src/world_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub(crate) fn world_query_impl(
}
}

// SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field
// SAFETY: `update_component_access` is called on every field
unsafe impl #user_impl_generics #path::query::WorldQuery
for #struct_name #user_ty_generics #user_where_clauses {

Expand Down
120 changes: 7 additions & 113 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
component::{ComponentId, Components, RequiredComponentConstructor, StorageType},
entity::{Entity, EntityLocation},
observer::Observers,
storage::{ImmutableSparseSet, SparseArray, SparseSet, SparseSetIndex, TableId, TableRow},
storage::{ImmutableSparseSet, SparseArray, SparseSet, TableId, TableRow},
};
use alloc::{boxed::Box, vec::Vec};
use bevy_platform::collections::HashMap;
Expand Down Expand Up @@ -349,7 +349,6 @@ pub(crate) struct ArchetypeSwapRemoveResult {
/// [`Component`]: crate::component::Component
struct ArchetypeComponentInfo {
storage_type: StorageType,
archetype_component_id: ArchetypeComponentId,
}

bitflags::bitflags! {
Expand Down Expand Up @@ -394,14 +393,14 @@ impl Archetype {
observers: &Observers,
id: ArchetypeId,
table_id: TableId,
table_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
sparse_set_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
table_components: impl Iterator<Item = ComponentId>,
sparse_set_components: impl Iterator<Item = ComponentId>,
) -> Self {
let (min_table, _) = table_components.size_hint();
let (min_sparse, _) = sparse_set_components.size_hint();
let mut flags = ArchetypeFlags::empty();
let mut archetype_components = SparseSet::with_capacity(min_table + min_sparse);
for (idx, (component_id, archetype_component_id)) in table_components.enumerate() {
for (idx, component_id) in table_components.enumerate() {
// SAFETY: We are creating an archetype that includes this component so it must exist
let info = unsafe { components.get_info_unchecked(component_id) };
info.update_archetype_flags(&mut flags);
Expand All @@ -410,7 +409,6 @@ impl Archetype {
component_id,
ArchetypeComponentInfo {
storage_type: StorageType::Table,
archetype_component_id,
},
);
// NOTE: the `table_components` are sorted AND they were inserted in the `Table` in the same
Expand All @@ -422,7 +420,7 @@ impl Archetype {
.insert(id, ArchetypeRecord { column: Some(idx) });
}

for (component_id, archetype_component_id) in sparse_set_components {
for component_id in sparse_set_components {
// SAFETY: We are creating an archetype that includes this component so it must exist
let info = unsafe { components.get_info_unchecked(component_id) };
info.update_archetype_flags(&mut flags);
Expand All @@ -431,7 +429,6 @@ impl Archetype {
component_id,
ArchetypeComponentInfo {
storage_type: StorageType::SparseSet,
archetype_component_id,
},
);
component_index
Expand Down Expand Up @@ -536,16 +533,6 @@ impl Archetype {
self.components.len()
}

/// Gets an iterator of all of the components in the archetype, along with
/// their archetype component ID.
pub(crate) fn components_with_archetype_component_id(
&self,
) -> impl Iterator<Item = (ComponentId, ArchetypeComponentId)> + '_ {
self.components
.iter()
.map(|(component_id, info)| (*component_id, info.archetype_component_id))
}

/// Fetches an immutable reference to the archetype's [`Edges`], a cache of
/// archetypal relationships.
#[inline]
Expand Down Expand Up @@ -664,19 +651,6 @@ impl Archetype {
.map(|info| info.storage_type)
}

/// Fetches the corresponding [`ArchetypeComponentId`] for a component in the archetype.
/// Returns `None` if the component is not part of the archetype.
/// This runs in `O(1)` time.
#[inline]
pub fn get_archetype_component_id(
&self,
component_id: ComponentId,
) -> Option<ArchetypeComponentId> {
self.components
.get(component_id)
.map(|info| info.archetype_component_id)
}

/// Clears all entities from the archetype.
pub(crate) fn clear_entities(&mut self) {
self.entities.clear();
Expand Down Expand Up @@ -774,46 +748,6 @@ struct ArchetypeComponents {
sparse_set_components: Box<[ComponentId]>,
}

/// An opaque unique joint ID for a [`Component`] in an [`Archetype`] within a [`World`].
///
/// A component may be present within multiple archetypes, but each component within
/// each archetype has its own unique `ArchetypeComponentId`. This is leveraged by the system
/// schedulers to opportunistically run multiple systems in parallel that would otherwise
/// conflict. For example, `Query<&mut A, With<B>>` and `Query<&mut A, Without<B>>` can run in
/// parallel as the matched `ArchetypeComponentId` sets for both queries are disjoint, even
/// though `&mut A` on both queries point to the same [`ComponentId`].
///
/// In SQL terms, these IDs are composite keys on a [many-to-many relationship] between archetypes
/// and components. Each component type will have only one [`ComponentId`], but may have many
/// [`ArchetypeComponentId`]s, one for every archetype the component is present in. Likewise, each
/// archetype will have only one [`ArchetypeId`] but may have many [`ArchetypeComponentId`]s, one
/// for each component that belongs to the archetype.
///
/// Every [`Resource`] is also assigned one of these IDs. As resources do not belong to any
/// particular archetype, a resource's ID uniquely identifies it.
///
/// These IDs are only valid within a given World, and are not globally unique.
/// Attempting to use an ID on a world that it wasn't sourced from will
/// not point to the same archetype nor the same component.
///
/// [`Component`]: crate::component::Component
/// [`World`]: crate::world::World
/// [`Resource`]: crate::resource::Resource
/// [many-to-many relationship]: https://en.wikipedia.org/wiki/Many-to-many_(data_model)
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ArchetypeComponentId(usize);

impl SparseSetIndex for ArchetypeComponentId {
#[inline]
fn sparse_set_index(&self) -> usize {
self.0
}

fn get_sparse_set_index(value: usize) -> Self {
Self(value)
}
}

/// Maps a [`ComponentId`] to the list of [`Archetypes`]([`Archetype`]) that contain the [`Component`](crate::component::Component),
/// along with an [`ArchetypeRecord`] which contains some metadata about how the component is stored in the archetype.
pub type ComponentIndex = HashMap<ComponentId, HashMap<ArchetypeId, ArchetypeRecord>>;
Expand All @@ -826,7 +760,6 @@ pub type ComponentIndex = HashMap<ComponentId, HashMap<ArchetypeId, ArchetypeRec
/// [module level documentation]: crate::archetype
pub struct Archetypes {
pub(crate) archetypes: Vec<Archetype>,
archetype_component_count: usize,
/// find the archetype id by the archetype's components
by_components: HashMap<ArchetypeComponents, ArchetypeId>,
/// find all the archetypes that contain a component
Expand All @@ -850,7 +783,6 @@ impl Archetypes {
archetypes: Vec::new(),
by_components: Default::default(),
by_component: Default::default(),
archetype_component_count: 0,
};
// SAFETY: Empty archetype has no components
unsafe {
Expand Down Expand Up @@ -905,22 +837,6 @@ impl Archetypes {
}
}

/// Generate and store a new [`ArchetypeComponentId`].
///
/// This simply increment the counter and return the new value.
///
/// # Panics
///
/// On archetype component id overflow.
pub(crate) fn new_archetype_component_id(&mut self) -> ArchetypeComponentId {
let id = ArchetypeComponentId(self.archetype_component_count);
self.archetype_component_count = self
.archetype_component_count
.checked_add(1)
.expect("archetype_component_count overflow");
id
}

/// Fetches an immutable reference to an [`Archetype`] using its
/// ID. Returns `None` if no corresponding archetype exists.
#[inline]
Expand Down Expand Up @@ -972,7 +888,6 @@ impl Archetypes {
};

let archetypes = &mut self.archetypes;
let archetype_component_count = &mut self.archetype_component_count;
let component_index = &mut self.by_component;
*self
.by_components
Expand All @@ -983,40 +898,19 @@ impl Archetypes {
sparse_set_components,
} = identity;
let id = ArchetypeId::new(archetypes.len());
let table_start = *archetype_component_count;
*archetype_component_count += table_components.len();
let table_archetype_components =
(table_start..*archetype_component_count).map(ArchetypeComponentId);
let sparse_start = *archetype_component_count;
*archetype_component_count += sparse_set_components.len();
let sparse_set_archetype_components =
(sparse_start..*archetype_component_count).map(ArchetypeComponentId);
archetypes.push(Archetype::new(
components,
component_index,
observers,
id,
table_id,
table_components
.iter()
.copied()
.zip(table_archetype_components),
sparse_set_components
.iter()
.copied()
.zip(sparse_set_archetype_components),
table_components.iter().copied(),
sparse_set_components.iter().copied(),
));
id
})
}

/// Returns the number of components that are stored in archetypes.
/// Note that if some component `T` is stored in more than one archetype, it will be counted once for each archetype it's present in.
#[inline]
pub fn archetype_components_len(&self) -> usize {
self.archetype_component_count
}

/// Clears all entities from all archetypes.
pub(crate) fn clear_entities(&mut self) {
for archetype in &mut self.archetypes {
Expand Down
9 changes: 0 additions & 9 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1228,7 +1228,6 @@ mod tests {
.components()
.get_resource_id(TypeId::of::<Num>())
.unwrap();
let archetype_component_id = world.storages().resources.get(resource_id).unwrap().id();

assert_eq!(world.resource::<Num>().0, 123);
assert!(world.contains_resource::<Num>());
Expand Down Expand Up @@ -1290,14 +1289,6 @@ mod tests {
resource_id, current_resource_id,
"resource id does not change after removing / re-adding"
);

let current_archetype_component_id =
world.storages().resources.get(resource_id).unwrap().id();

assert_eq!(
archetype_component_id, current_archetype_component_id,
"resource archetype component id does not change after removing / re-adding"
);
}

#[test]
Expand Down
2 changes: 0 additions & 2 deletions crates/bevy_ecs/src/observer/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,13 +366,11 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
};

// SAFETY:
// - `update_archetype_component_access` is called first
// - there are no outstanding references to world except a private component
// - system is an `ObserverSystem` so won't mutate world beyond the access of a `DeferredWorld`
// and is never exclusive
// - system is the same type erased system from above
unsafe {
(*system).update_archetype_component_access(world);
match (*system).validate_param_unsafe(world) {
Ok(()) => {
if let Err(err) = (*system).run_unsafe(trigger, world) {
Expand Down
Loading