Skip to content

Add a public API to ArchetypeGeneration/Id #9825

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 3 commits into from
Oct 2, 2023
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
68 changes: 46 additions & 22 deletions crates/bevy_ecs/src/archetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
};
use std::{
hash::Hash,
ops::{Index, IndexMut},
ops::{Index, IndexMut, RangeFrom},
};

/// An opaque location within a [`Archetype`].
Expand Down Expand Up @@ -70,7 +70,7 @@ impl ArchetypeRow {
///
/// [`World`]: crate::world::World
/// [`EMPTY`]: crate::archetype::ArchetypeId::EMPTY
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
// SAFETY: Must be repr(transparent) due to the safety requirements on EntityLocation
#[repr(transparent)]
pub struct ArchetypeId(u32);
Expand All @@ -83,13 +83,26 @@ impl ArchetypeId {
/// This must always have an all-1s bit pattern to ensure soundness in fast entity id space allocation.
pub const INVALID: ArchetypeId = ArchetypeId(u32::MAX);

/// Create an `ArchetypeId` from a plain value.
///
/// This is useful if you need to store the `ArchetypeId` as a plain value,
/// for example in a specialized data structure such as a bitset.
///
/// While it doesn't break any safety invariants, you should ensure the
/// values comes from a pre-existing [`ArchetypeId::index`] in this world
/// to avoid panics and other unexpected behaviors.
#[inline]
pub(crate) const fn new(index: usize) -> Self {
pub const fn new(index: usize) -> Self {
ArchetypeId(index as u32)
}

/// The plain value of this `ArchetypeId`.
///
/// In bevy, this is mostly used to store archetype ids in [`FixedBitSet`]s.
///
/// [`FixedBitSet`]: fixedbitset::FixedBitSet
#[inline]
pub(crate) fn index(self) -> usize {
pub fn index(self) -> usize {
self.0 as usize
}
}
Expand Down Expand Up @@ -525,24 +538,23 @@ impl Archetype {
}
}

/// An opaque generational id that changes every time the set of [`Archetypes`] changes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct ArchetypeGeneration(usize);
/// The next [`ArchetypeId`] in an [`Archetypes`] collection.
///
/// This is used in archetype update methods to limit archetype updates to the
/// ones added since the last time the method ran.
#[derive(Debug, Copy, Clone)]
pub struct ArchetypeGeneration(ArchetypeId);

impl ArchetypeGeneration {
/// The first archetype.
#[inline]
pub(crate) const fn initial() -> Self {
ArchetypeGeneration(0)
}

#[inline]
pub(crate) fn value(self) -> usize {
self.0
pub const fn initial() -> Self {
ArchetypeGeneration(ArchetypeId::EMPTY)
}
}

#[derive(Hash, PartialEq, Eq)]
struct ArchetypeIdentity {
struct ArchetypeComponents {
table_components: Box<[ComponentId]>,
sparse_set_components: Box<[ComponentId]>,
}
Expand Down Expand Up @@ -603,25 +615,29 @@ impl SparseSetIndex for ArchetypeComponentId {
pub struct Archetypes {
pub(crate) archetypes: Vec<Archetype>,
pub(crate) archetype_component_count: usize,
archetype_ids: bevy_utils::HashMap<ArchetypeIdentity, ArchetypeId>,
by_components: bevy_utils::HashMap<ArchetypeComponents, ArchetypeId>,
}

impl Archetypes {
pub(crate) fn new() -> Self {
let mut archetypes = Archetypes {
archetypes: Vec::new(),
archetype_ids: Default::default(),
by_components: Default::default(),
archetype_component_count: 0,
};
archetypes.get_id_or_insert(TableId::empty(), Vec::new(), Vec::new());
archetypes
}

/// Returns the current archetype generation. This is an ID indicating the current set of archetypes
/// that are registered with the world.
/// Returns the "generation", a handle to the current highest archetype ID.
///
/// This can be used with the `Index` [`Archetypes`] implementation to
/// iterate over newly introduced [`Archetype`]s since the last time this
/// function was called.
#[inline]
pub fn generation(&self) -> ArchetypeGeneration {
ArchetypeGeneration(self.archetypes.len())
let id = ArchetypeId::new(self.archetypes.len());
ArchetypeGeneration(id)
}

/// Fetches the total number of [`Archetype`]s within the world.
Expand Down Expand Up @@ -692,15 +708,15 @@ impl Archetypes {
table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>,
) -> ArchetypeId {
let archetype_identity = ArchetypeIdentity {
let archetype_identity = ArchetypeComponents {
sparse_set_components: sparse_set_components.clone().into_boxed_slice(),
table_components: table_components.clone().into_boxed_slice(),
};

let archetypes = &mut self.archetypes;
let archetype_component_count = &mut self.archetype_component_count;
*self
.archetype_ids
.by_components
.entry(archetype_identity)
.or_insert_with(move || {
let id = ArchetypeId::new(archetypes.len());
Expand Down Expand Up @@ -739,6 +755,14 @@ impl Archetypes {
}
}

impl Index<RangeFrom<ArchetypeGeneration>> for Archetypes {
type Output = [Archetype];

#[inline]
fn index(&self, index: RangeFrom<ArchetypeGeneration>) -> &Self::Output {
&self.archetypes[index.start.0.index()..]
}
}
impl Index<ArchetypeId> for Archetypes {
type Output = Archetype;

Expand Down
9 changes: 4 additions & 5 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,11 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
self.validate_world(world.id());
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();
let old_generation =
std::mem::replace(&mut self.archetype_generation, archetypes.generation());

for archetype_index in archetype_index_range {
self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]);
for archetype in &archetypes[old_generation..] {
self.new_archetype(archetype);
}
}

Expand Down
30 changes: 10 additions & 20 deletions crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
archetype::{ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
archetype::{ArchetypeComponentId, ArchetypeGeneration},
component::{ComponentId, Tick},
prelude::FromWorld,
query::{Access, FilteredAccessSet},
Expand Down Expand Up @@ -256,16 +256,11 @@ impl<Param: SystemParam> SystemState<Param> {
#[inline]
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();

for archetype_index in archetype_index_range {
Param::new_archetype(
&mut self.param_state,
&archetypes[ArchetypeId::new(archetype_index)],
&mut self.meta,
);
let old_generation =
std::mem::replace(&mut self.archetype_generation, archetypes.generation());

for archetype in &archetypes[old_generation..] {
Param::new_archetype(&mut self.param_state, archetype, &mut self.meta);
}
}

Expand Down Expand Up @@ -501,17 +496,12 @@ where
fn update_archetype_component_access(&mut self, world: UnsafeWorldCell) {
assert!(self.world_id == Some(world.id()), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();
let old_generation =
std::mem::replace(&mut self.archetype_generation, archetypes.generation());

for archetype_index in archetype_index_range {
for archetype in &archetypes[old_generation..] {
let param_state = self.param_state.as_mut().unwrap();
F::Param::new_archetype(
param_state,
&archetypes[ArchetypeId::new(archetype_index)],
&mut self.system_meta,
);
F::Param::new_archetype(param_state, archetype, &mut self.system_meta);
}
}

Expand Down