Misc. docs and renames for niche ECS internals (#16786)
## Objective Some structs and methods in the ECS internals have names that don't describe their purpose very well, and sometimes don't have docs either. Also, the function `remove_bundle_from_archetype` is a counterpart to `BundleInfo::add_bundle_to_archetype`, but isn't a method and is in a different file. ## Solution - Renamed the following structs and added docs: | Before | After | |----------------------|------------------------------| | `AddBundle` | `ArchetypeAfterBundleInsert` | | `InsertBundleResult` | `ArchetypeMoveType` | - Renamed the following methods: | Before | After | |---------------------------------------|----------------------------------------------| | `Edges::get_add_bundle` | `Edges::get_archetype_after_bundle_insert` | | `Edges::insert_add_bundle` | `Edges::cache_archetype_after_bundle_insert` | | `Edges::get_remove_bundle` | `Edges::get_archetype_after_bundle_remove` | | `Edges::insert_remove_bundle` | `Edges::cache_archetype_after_bundle_remove` | | `Edges::get_take_bundle` | `Edges::get_archetype_after_bundle_take` | | `Edges::insert_take_bundle` | `Edges::cache_archetype_after_bundle_take` | - Moved `remove_bundle_from_archetype` from `world/entity_ref.rs` to `BundleInfo`. I left the function in entity_ref in the first commit for comparison, look there for the diff of comments and whatnot. - Tidied up docs: - General grammar and spacing. - Made the usage of "insert" and "add" more consistent. - Removed references to information that isn't there. - Renamed `BundleInfo::add_bundle_to_archetype` to `BundleInfo::insert_bundle_into_archetype` for consistency.
This commit is contained in:
parent
ced6159d93
commit
d132239bb1
@ -109,7 +109,7 @@ impl ArchetypeId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in [`AddBundle`] to track whether components in the bundle are newly
|
/// Used in [`ArchetypeAfterBundleInsert`] to track whether components in the bundle are newly
|
||||||
/// added or already existed in the entity's archetype.
|
/// added or already existed in the entity's archetype.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
pub(crate) enum ComponentStatus {
|
pub(crate) enum ComponentStatus {
|
||||||
@ -117,11 +117,12 @@ pub(crate) enum ComponentStatus {
|
|||||||
Existing,
|
Existing,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct AddBundle {
|
/// Used in [`Edges`] to cache the result of inserting a bundle into the source archetype.
|
||||||
/// The target archetype after the bundle is added to the source archetype
|
pub(crate) struct ArchetypeAfterBundleInsert {
|
||||||
|
/// The target archetype after the bundle is inserted into the source archetype.
|
||||||
pub archetype_id: ArchetypeId,
|
pub archetype_id: ArchetypeId,
|
||||||
/// For each component iterated in the same order as the source [`Bundle`](crate::bundle::Bundle),
|
/// For each component iterated in the same order as the source [`Bundle`](crate::bundle::Bundle),
|
||||||
/// indicate if the component is newly added to the target archetype or if it already existed
|
/// indicate if the component is newly added to the target archetype or if it already existed.
|
||||||
pub bundle_status: Vec<ComponentStatus>,
|
pub bundle_status: Vec<ComponentStatus>,
|
||||||
/// The set of additional required components that must be initialized immediately when adding this Bundle.
|
/// The set of additional required components that must be initialized immediately when adding this Bundle.
|
||||||
///
|
///
|
||||||
@ -134,7 +135,7 @@ pub(crate) struct AddBundle {
|
|||||||
pub existing: Vec<ComponentId>,
|
pub existing: Vec<ComponentId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddBundle {
|
impl ArchetypeAfterBundleInsert {
|
||||||
pub(crate) fn iter_inserted(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
|
pub(crate) fn iter_inserted(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
|
||||||
self.added.iter().chain(self.existing.iter()).copied()
|
self.added.iter().chain(self.existing.iter()).copied()
|
||||||
}
|
}
|
||||||
@ -149,10 +150,10 @@ impl AddBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This trait is used to report the status of [`Bundle`](crate::bundle::Bundle) components
|
/// This trait is used to report the status of [`Bundle`](crate::bundle::Bundle) components
|
||||||
/// being added to a given entity, relative to that entity's original archetype.
|
/// being inserted into a given entity, relative to that entity's original archetype.
|
||||||
/// See [`crate::bundle::BundleInfo::write_components`] for more info.
|
/// See [`crate::bundle::BundleInfo::write_components`] for more info.
|
||||||
pub(crate) trait BundleComponentStatus {
|
pub(crate) trait BundleComponentStatus {
|
||||||
/// Returns the Bundle's component status for the given "bundle index"
|
/// Returns the Bundle's component status for the given "bundle index".
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Callers must ensure that index is always a valid bundle index for the
|
/// Callers must ensure that index is always a valid bundle index for the
|
||||||
@ -160,7 +161,7 @@ pub(crate) trait BundleComponentStatus {
|
|||||||
unsafe fn get_status(&self, index: usize) -> ComponentStatus;
|
unsafe fn get_status(&self, index: usize) -> ComponentStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BundleComponentStatus for AddBundle {
|
impl BundleComponentStatus for ArchetypeAfterBundleInsert {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_status(&self, index: usize) -> ComponentStatus {
|
unsafe fn get_status(&self, index: usize) -> ComponentStatus {
|
||||||
// SAFETY: caller has ensured index is a valid bundle index for this bundle
|
// SAFETY: caller has ensured index is a valid bundle index for this bundle
|
||||||
@ -173,7 +174,7 @@ pub(crate) struct SpawnBundleStatus;
|
|||||||
impl BundleComponentStatus for SpawnBundleStatus {
|
impl BundleComponentStatus for SpawnBundleStatus {
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn get_status(&self, _index: usize) -> ComponentStatus {
|
unsafe fn get_status(&self, _index: usize) -> ComponentStatus {
|
||||||
// Components added during a spawn call are always treated as added
|
// Components inserted during a spawn call are always treated as added.
|
||||||
ComponentStatus::Added
|
ComponentStatus::Added
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,37 +195,36 @@ impl BundleComponentStatus for SpawnBundleStatus {
|
|||||||
/// [`World`]: crate::world::World
|
/// [`World`]: crate::world::World
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Edges {
|
pub struct Edges {
|
||||||
add_bundle: SparseArray<BundleId, AddBundle>,
|
insert_bundle: SparseArray<BundleId, ArchetypeAfterBundleInsert>,
|
||||||
remove_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
|
remove_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
|
||||||
take_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
|
take_bundle: SparseArray<BundleId, Option<ArchetypeId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edges {
|
impl Edges {
|
||||||
/// Checks the cache for the target archetype when adding a bundle to the
|
/// Checks the cache for the target archetype when inserting a bundle into the
|
||||||
/// source archetype. For more information, see [`EntityWorldMut::insert`].
|
/// source archetype.
|
||||||
///
|
///
|
||||||
/// If this returns `None`, it means there has not been a transition from
|
/// If this returns `None`, it means there has not been a transition from
|
||||||
/// the source archetype via the provided bundle.
|
/// the source archetype via the provided bundle.
|
||||||
///
|
|
||||||
/// [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_add_bundle(&self, bundle_id: BundleId) -> Option<ArchetypeId> {
|
pub fn get_archetype_after_bundle_insert(&self, bundle_id: BundleId) -> Option<ArchetypeId> {
|
||||||
self.get_add_bundle_internal(bundle_id)
|
self.get_archetype_after_bundle_insert_internal(bundle_id)
|
||||||
.map(|bundle| bundle.archetype_id)
|
.map(|bundle| bundle.archetype_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal version of `get_add_bundle` that fetches the full `AddBundle`.
|
/// Internal version of `get_archetype_after_bundle_insert` that
|
||||||
|
/// fetches the full `ArchetypeAfterBundleInsert`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn get_add_bundle_internal(&self, bundle_id: BundleId) -> Option<&AddBundle> {
|
pub(crate) fn get_archetype_after_bundle_insert_internal(
|
||||||
self.add_bundle.get(bundle_id)
|
&self,
|
||||||
|
bundle_id: BundleId,
|
||||||
|
) -> Option<&ArchetypeAfterBundleInsert> {
|
||||||
|
self.insert_bundle.get(bundle_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Caches the target archetype when adding a bundle to the source archetype.
|
/// Caches the target archetype when inserting a bundle into the source archetype.
|
||||||
/// For more information, see [`EntityWorldMut::insert`].
|
|
||||||
///
|
|
||||||
/// [`EntityWorldMut::insert`]: crate::world::EntityWorldMut::insert
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn insert_add_bundle(
|
pub(crate) fn cache_archetype_after_bundle_insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
bundle_id: BundleId,
|
bundle_id: BundleId,
|
||||||
archetype_id: ArchetypeId,
|
archetype_id: ArchetypeId,
|
||||||
@ -233,9 +233,9 @@ impl Edges {
|
|||||||
added: Vec<ComponentId>,
|
added: Vec<ComponentId>,
|
||||||
existing: Vec<ComponentId>,
|
existing: Vec<ComponentId>,
|
||||||
) {
|
) {
|
||||||
self.add_bundle.insert(
|
self.insert_bundle.insert(
|
||||||
bundle_id,
|
bundle_id,
|
||||||
AddBundle {
|
ArchetypeAfterBundleInsert {
|
||||||
archetype_id,
|
archetype_id,
|
||||||
bundle_status,
|
bundle_status,
|
||||||
required_components,
|
required_components,
|
||||||
@ -245,27 +245,25 @@ impl Edges {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the cache for the target archetype when removing a bundle to the
|
/// Checks the cache for the target archetype when removing a bundle from the
|
||||||
/// source archetype. For more information, see [`EntityWorldMut::remove`].
|
/// source archetype.
|
||||||
///
|
///
|
||||||
/// If this returns `None`, it means there has not been a transition from
|
/// If this returns `None`, it means there has not been a transition from
|
||||||
/// the source archetype via the provided bundle.
|
/// the source archetype via the provided bundle.
|
||||||
///
|
///
|
||||||
/// If this returns `Some(None)`, it means that the bundle cannot be removed
|
/// If this returns `Some(None)`, it means that the bundle cannot be removed
|
||||||
/// from the source archetype.
|
/// from the source archetype.
|
||||||
///
|
|
||||||
/// [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_remove_bundle(&self, bundle_id: BundleId) -> Option<Option<ArchetypeId>> {
|
pub fn get_archetype_after_bundle_remove(
|
||||||
|
&self,
|
||||||
|
bundle_id: BundleId,
|
||||||
|
) -> Option<Option<ArchetypeId>> {
|
||||||
self.remove_bundle.get(bundle_id).cloned()
|
self.remove_bundle.get(bundle_id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Caches the target archetype when removing a bundle to the source archetype.
|
/// Caches the target archetype when removing a bundle from the source archetype.
|
||||||
/// For more information, see [`EntityWorldMut::remove`].
|
|
||||||
///
|
|
||||||
/// [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn insert_remove_bundle(
|
pub(crate) fn cache_archetype_after_bundle_remove(
|
||||||
&mut self,
|
&mut self,
|
||||||
bundle_id: BundleId,
|
bundle_id: BundleId,
|
||||||
archetype_id: Option<ArchetypeId>,
|
archetype_id: Option<ArchetypeId>,
|
||||||
@ -273,24 +271,31 @@ impl Edges {
|
|||||||
self.remove_bundle.insert(bundle_id, archetype_id);
|
self.remove_bundle.insert(bundle_id, archetype_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the cache for the target archetype when removing a bundle to the
|
/// Checks the cache for the target archetype when taking a bundle from the
|
||||||
/// source archetype. For more information, see [`EntityWorldMut::remove`].
|
/// source archetype.
|
||||||
|
///
|
||||||
|
/// Unlike `remove`, `take` will only succeed if the source archetype
|
||||||
|
/// contains all of the components in the bundle.
|
||||||
///
|
///
|
||||||
/// If this returns `None`, it means there has not been a transition from
|
/// If this returns `None`, it means there has not been a transition from
|
||||||
/// the source archetype via the provided bundle.
|
/// the source archetype via the provided bundle.
|
||||||
///
|
///
|
||||||
/// [`EntityWorldMut::remove`]: crate::world::EntityWorldMut::remove
|
/// If this returns `Some(None)`, it means that the bundle cannot be taken
|
||||||
|
/// from the source archetype.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_take_bundle(&self, bundle_id: BundleId) -> Option<Option<ArchetypeId>> {
|
pub fn get_archetype_after_bundle_take(
|
||||||
|
&self,
|
||||||
|
bundle_id: BundleId,
|
||||||
|
) -> Option<Option<ArchetypeId>> {
|
||||||
self.take_bundle.get(bundle_id).cloned()
|
self.take_bundle.get(bundle_id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Caches the target archetype when removing a bundle to the source archetype.
|
/// Caches the target archetype when taking a bundle from the source archetype.
|
||||||
/// For more information, see [`EntityWorldMut::take`].
|
|
||||||
///
|
///
|
||||||
/// [`EntityWorldMut::take`]: crate::world::EntityWorldMut::take
|
/// Unlike `remove`, `take` will only succeed if the source archetype
|
||||||
|
/// contains all of the components in the bundle.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn insert_take_bundle(
|
pub(crate) fn cache_archetype_after_bundle_take(
|
||||||
&mut self,
|
&mut self,
|
||||||
bundle_id: BundleId,
|
bundle_id: BundleId,
|
||||||
archetype_id: Option<ArchetypeId>,
|
archetype_id: Option<ArchetypeId>,
|
||||||
@ -577,11 +582,11 @@ impl Archetype {
|
|||||||
self.entities.reserve(additional);
|
self.entities.reserve(additional);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the entity at `index` by swapping it out. Returns the table row the entity is stored
|
/// Removes the entity at `row` by swapping it out. Returns the table row the entity is stored
|
||||||
/// in.
|
/// in.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// This function will panic if `index >= self.len()`
|
/// This function will panic if `row >= self.entities.len()`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn swap_remove(&mut self, row: ArchetypeRow) -> ArchetypeSwapRemoveResult {
|
pub(crate) fn swap_remove(&mut self, row: ArchetypeRow) -> ArchetypeSwapRemoveResult {
|
||||||
let is_last = row.index() == self.entities.len() - 1;
|
let is_last = row.index() == self.entities.len() - 1;
|
||||||
|
@ -6,8 +6,8 @@ pub use bevy_ecs_macros::Bundle;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::{
|
archetype::{
|
||||||
AddBundle, Archetype, ArchetypeId, Archetypes, BundleComponentStatus, ComponentStatus,
|
Archetype, ArchetypeAfterBundleInsert, ArchetypeId, Archetypes, BundleComponentStatus,
|
||||||
SpawnBundleStatus,
|
ComponentStatus, SpawnBundleStatus,
|
||||||
},
|
},
|
||||||
component::{
|
component::{
|
||||||
Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents,
|
Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents,
|
||||||
@ -488,13 +488,16 @@ impl BundleInfo {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component
|
/// `bundle_component_status` must return the "correct" [`ComponentStatus`] for each component
|
||||||
/// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added)
|
/// in the [`Bundle`], with respect to the entity's original archetype (prior to the bundle being added).
|
||||||
|
///
|
||||||
/// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status
|
/// For example, if the original archetype already has `ComponentA` and `T` also has `ComponentA`, the status
|
||||||
/// should be `Mutated`. If the original archetype does not have `ComponentA`, the status should be `Added`.
|
/// should be `Existing`. If the original archetype does not have `ComponentA`, the status should be `Added`.
|
||||||
/// When "inserting" a bundle into an existing entity, [`AddBundle`]
|
///
|
||||||
/// should be used, which will report `Added` vs `Mutated` status based on the current archetype's structure.
|
/// When "inserting" a bundle into an existing entity, [`ArchetypeAfterBundleInsert`]
|
||||||
|
/// should be used, which will report `Added` vs `Existing` status based on the current archetype's structure.
|
||||||
|
///
|
||||||
/// When spawning a bundle, [`SpawnBundleStatus`] can be used instead, which removes the need
|
/// When spawning a bundle, [`SpawnBundleStatus`] can be used instead, which removes the need
|
||||||
/// to look up the [`AddBundle`] in the archetype graph, which requires
|
/// to look up the [`ArchetypeAfterBundleInsert`] in the archetype graph, which requires
|
||||||
/// ownership of the entity's current archetype.
|
/// ownership of the entity's current archetype.
|
||||||
///
|
///
|
||||||
/// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the
|
/// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the
|
||||||
@ -634,12 +637,15 @@ impl BundleInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the
|
/// Inserts a bundle into the given archetype and returns the resulting archetype.
|
||||||
/// same [`ArchetypeId`], in the event that adding the given bundle does not result in an
|
/// This could be the same [`ArchetypeId`], in the event that inserting the given bundle
|
||||||
/// [`Archetype`] change. Results are cached in the [`Archetype`] graph to avoid redundant work.
|
/// does not result in an [`Archetype`] change.
|
||||||
|
///
|
||||||
|
/// Results are cached in the [`Archetype`] graph to avoid redundant work.
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `components` must be the same components as passed in [`Self::new`]
|
/// `components` must be the same components as passed in [`Self::new`]
|
||||||
pub(crate) unsafe fn add_bundle_to_archetype(
|
pub(crate) unsafe fn insert_bundle_into_archetype(
|
||||||
&self,
|
&self,
|
||||||
archetypes: &mut Archetypes,
|
archetypes: &mut Archetypes,
|
||||||
storages: &mut Storages,
|
storages: &mut Storages,
|
||||||
@ -647,8 +653,11 @@ impl BundleInfo {
|
|||||||
observers: &Observers,
|
observers: &Observers,
|
||||||
archetype_id: ArchetypeId,
|
archetype_id: ArchetypeId,
|
||||||
) -> ArchetypeId {
|
) -> ArchetypeId {
|
||||||
if let Some(add_bundle_id) = archetypes[archetype_id].edges().get_add_bundle(self.id) {
|
if let Some(archetype_after_insert_id) = archetypes[archetype_id]
|
||||||
return add_bundle_id;
|
.edges()
|
||||||
|
.get_archetype_after_bundle_insert(self.id)
|
||||||
|
{
|
||||||
|
return archetype_after_insert_id;
|
||||||
}
|
}
|
||||||
let mut new_table_components = Vec::new();
|
let mut new_table_components = Vec::new();
|
||||||
let mut new_sparse_set_components = Vec::new();
|
let mut new_sparse_set_components = Vec::new();
|
||||||
@ -693,8 +702,8 @@ impl BundleInfo {
|
|||||||
|
|
||||||
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
|
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
|
||||||
let edges = current_archetype.edges_mut();
|
let edges = current_archetype.edges_mut();
|
||||||
// the archetype does not change when we add this bundle
|
// The archetype does not change when we insert this bundle.
|
||||||
edges.insert_add_bundle(
|
edges.cache_archetype_after_bundle_insert(
|
||||||
self.id,
|
self.id,
|
||||||
archetype_id,
|
archetype_id,
|
||||||
bundle_status,
|
bundle_status,
|
||||||
@ -707,16 +716,16 @@ impl BundleInfo {
|
|||||||
let table_id;
|
let table_id;
|
||||||
let table_components;
|
let table_components;
|
||||||
let sparse_set_components;
|
let sparse_set_components;
|
||||||
// the archetype changes when we add this bundle. prepare the new archetype and storages
|
// The archetype changes when we insert this bundle. Prepare the new archetype and storages.
|
||||||
{
|
{
|
||||||
let current_archetype = &archetypes[archetype_id];
|
let current_archetype = &archetypes[archetype_id];
|
||||||
table_components = if new_table_components.is_empty() {
|
table_components = if new_table_components.is_empty() {
|
||||||
// if there are no new table components, we can keep using this table
|
// If there are no new table components, we can keep using this table.
|
||||||
table_id = current_archetype.table_id();
|
table_id = current_archetype.table_id();
|
||||||
current_archetype.table_components().collect()
|
current_archetype.table_components().collect()
|
||||||
} else {
|
} else {
|
||||||
new_table_components.extend(current_archetype.table_components());
|
new_table_components.extend(current_archetype.table_components());
|
||||||
// sort to ignore order while hashing
|
// Sort to ignore order while hashing.
|
||||||
new_table_components.sort_unstable();
|
new_table_components.sort_unstable();
|
||||||
// SAFETY: all component ids in `new_table_components` exist
|
// SAFETY: all component ids in `new_table_components` exist
|
||||||
table_id = unsafe {
|
table_id = unsafe {
|
||||||
@ -732,7 +741,7 @@ impl BundleInfo {
|
|||||||
current_archetype.sparse_set_components().collect()
|
current_archetype.sparse_set_components().collect()
|
||||||
} else {
|
} else {
|
||||||
new_sparse_set_components.extend(current_archetype.sparse_set_components());
|
new_sparse_set_components.extend(current_archetype.sparse_set_components());
|
||||||
// sort to ignore order while hashing
|
// Sort to ignore order while hashing.
|
||||||
new_sparse_set_components.sort_unstable();
|
new_sparse_set_components.sort_unstable();
|
||||||
new_sparse_set_components
|
new_sparse_set_components
|
||||||
};
|
};
|
||||||
@ -745,8 +754,10 @@ impl BundleInfo {
|
|||||||
table_components,
|
table_components,
|
||||||
sparse_set_components,
|
sparse_set_components,
|
||||||
);
|
);
|
||||||
// add an edge from the old archetype to the new archetype
|
// Add an edge from the old archetype to the new archetype.
|
||||||
archetypes[archetype_id].edges_mut().insert_add_bundle(
|
archetypes[archetype_id]
|
||||||
|
.edges_mut()
|
||||||
|
.cache_archetype_after_bundle_insert(
|
||||||
self.id,
|
self.id,
|
||||||
new_archetype_id,
|
new_archetype_id,
|
||||||
bundle_status,
|
bundle_status,
|
||||||
@ -757,24 +768,142 @@ impl BundleInfo {
|
|||||||
new_archetype_id
|
new_archetype_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes a bundle from the given archetype and returns the resulting archetype
|
||||||
|
/// (or `None` if the removal was invalid).
|
||||||
|
/// This could be the same [`ArchetypeId`], in the event that removing the given bundle
|
||||||
|
/// does not result in an [`Archetype`] change.
|
||||||
|
///
|
||||||
|
/// Results are cached in the [`Archetype`] graph to avoid redundant work.
|
||||||
|
///
|
||||||
|
/// If `intersection` is false, attempting to remove a bundle with components not contained in the
|
||||||
|
/// current archetype will fail, returning `None`.
|
||||||
|
///
|
||||||
|
/// If `intersection` is true, components in the bundle but not in the current archetype
|
||||||
|
/// will be ignored.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `archetype_id` must exist and components in `bundle_info` must exist
|
||||||
|
pub(crate) unsafe fn remove_bundle_from_archetype(
|
||||||
|
&self,
|
||||||
|
archetypes: &mut Archetypes,
|
||||||
|
storages: &mut Storages,
|
||||||
|
components: &Components,
|
||||||
|
observers: &Observers,
|
||||||
|
archetype_id: ArchetypeId,
|
||||||
|
intersection: bool,
|
||||||
|
) -> Option<ArchetypeId> {
|
||||||
|
// Check the archetype graph to see if the bundle has been
|
||||||
|
// removed from this archetype in the past.
|
||||||
|
let archetype_after_remove_result = {
|
||||||
|
let edges = archetypes[archetype_id].edges();
|
||||||
|
if intersection {
|
||||||
|
edges.get_archetype_after_bundle_remove(self.id())
|
||||||
|
} else {
|
||||||
|
edges.get_archetype_after_bundle_take(self.id())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = if let Some(result) = archetype_after_remove_result {
|
||||||
|
// This bundle removal result is cached. Just return that!
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let mut next_table_components;
|
||||||
|
let mut next_sparse_set_components;
|
||||||
|
let next_table_id;
|
||||||
|
{
|
||||||
|
let current_archetype = &mut archetypes[archetype_id];
|
||||||
|
let mut removed_table_components = Vec::new();
|
||||||
|
let mut removed_sparse_set_components = Vec::new();
|
||||||
|
for component_id in self.iter_explicit_components() {
|
||||||
|
if current_archetype.contains(component_id) {
|
||||||
|
// SAFETY: bundle components were already initialized by bundles.get_info
|
||||||
|
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
||||||
|
match component_info.storage_type() {
|
||||||
|
StorageType::Table => removed_table_components.push(component_id),
|
||||||
|
StorageType::SparseSet => {
|
||||||
|
removed_sparse_set_components.push(component_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !intersection {
|
||||||
|
// A component in the bundle was not present in the entity's archetype, so this
|
||||||
|
// removal is invalid. Cache the result in the archetype graph.
|
||||||
|
current_archetype
|
||||||
|
.edges_mut()
|
||||||
|
.cache_archetype_after_bundle_take(self.id(), None);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort removed components so we can do an efficient "sorted remove".
|
||||||
|
// Archetype components are already sorted.
|
||||||
|
removed_table_components.sort_unstable();
|
||||||
|
removed_sparse_set_components.sort_unstable();
|
||||||
|
next_table_components = current_archetype.table_components().collect();
|
||||||
|
next_sparse_set_components = current_archetype.sparse_set_components().collect();
|
||||||
|
sorted_remove(&mut next_table_components, &removed_table_components);
|
||||||
|
sorted_remove(
|
||||||
|
&mut next_sparse_set_components,
|
||||||
|
&removed_sparse_set_components,
|
||||||
|
);
|
||||||
|
|
||||||
|
next_table_id = if removed_table_components.is_empty() {
|
||||||
|
current_archetype.table_id()
|
||||||
|
} else {
|
||||||
|
// SAFETY: all components in next_table_components exist
|
||||||
|
unsafe {
|
||||||
|
storages
|
||||||
|
.tables
|
||||||
|
.get_id_or_insert(&next_table_components, components)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_archetype_id = archetypes.get_id_or_insert(
|
||||||
|
components,
|
||||||
|
observers,
|
||||||
|
next_table_id,
|
||||||
|
next_table_components,
|
||||||
|
next_sparse_set_components,
|
||||||
|
);
|
||||||
|
Some(new_archetype_id)
|
||||||
|
};
|
||||||
|
let current_archetype = &mut archetypes[archetype_id];
|
||||||
|
// Cache the result in an edge.
|
||||||
|
if intersection {
|
||||||
|
current_archetype
|
||||||
|
.edges_mut()
|
||||||
|
.cache_archetype_after_bundle_remove(self.id(), result);
|
||||||
|
} else {
|
||||||
|
current_archetype
|
||||||
|
.edges_mut()
|
||||||
|
.cache_archetype_after_bundle_take(self.id(), result);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: We have exclusive world access so our pointers can't be invalidated externally
|
// SAFETY: We have exclusive world access so our pointers can't be invalidated externally
|
||||||
pub(crate) struct BundleInserter<'w> {
|
pub(crate) struct BundleInserter<'w> {
|
||||||
world: UnsafeWorldCell<'w>,
|
world: UnsafeWorldCell<'w>,
|
||||||
bundle_info: ConstNonNull<BundleInfo>,
|
bundle_info: ConstNonNull<BundleInfo>,
|
||||||
add_bundle: ConstNonNull<AddBundle>,
|
archetype_after_insert: ConstNonNull<ArchetypeAfterBundleInsert>,
|
||||||
table: NonNull<Table>,
|
table: NonNull<Table>,
|
||||||
archetype: NonNull<Archetype>,
|
archetype: NonNull<Archetype>,
|
||||||
result: InsertBundleResult,
|
archetype_move_type: ArchetypeMoveType,
|
||||||
change_tick: Tick,
|
change_tick: Tick,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) enum InsertBundleResult {
|
/// The type of archetype move (or lack thereof) that will result from a bundle
|
||||||
|
/// being inserted into an entity.
|
||||||
|
pub(crate) enum ArchetypeMoveType {
|
||||||
|
/// If the entity already has all of the components that are being inserted,
|
||||||
|
/// its archetype won't change.
|
||||||
SameArchetype,
|
SameArchetype,
|
||||||
NewArchetypeSameTable {
|
/// If only [`sparse set`](StorageType::SparseSet) components are being added,
|
||||||
new_archetype: NonNull<Archetype>,
|
/// the entity's archetype will change while keeping the same table.
|
||||||
},
|
NewArchetypeSameTable { new_archetype: NonNull<Archetype> },
|
||||||
|
/// If any [`table-stored`](StorageType::Table) components are being added,
|
||||||
|
/// both the entity's archetype and table will change.
|
||||||
NewArchetypeNewTable {
|
NewArchetypeNewTable {
|
||||||
new_archetype: NonNull<Archetype>,
|
new_archetype: NonNull<Archetype>,
|
||||||
new_table: NonNull<Table>,
|
new_table: NonNull<Table>,
|
||||||
@ -809,7 +938,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
// SAFETY: We will not make any accesses to the command queue, component or resource data of this world
|
// SAFETY: We will not make any accesses to the command queue, component or resource data of this world
|
||||||
let bundle_info = world.bundles.get_unchecked(bundle_id);
|
let bundle_info = world.bundles.get_unchecked(bundle_id);
|
||||||
let bundle_id = bundle_info.id();
|
let bundle_id = bundle_info.id();
|
||||||
let new_archetype_id = bundle_info.add_bundle_to_archetype(
|
let new_archetype_id = bundle_info.insert_bundle_into_archetype(
|
||||||
&mut world.archetypes,
|
&mut world.archetypes,
|
||||||
&mut world.storages,
|
&mut world.storages,
|
||||||
&world.components,
|
&world.components,
|
||||||
@ -818,32 +947,32 @@ impl<'w> BundleInserter<'w> {
|
|||||||
);
|
);
|
||||||
if new_archetype_id == archetype_id {
|
if new_archetype_id == archetype_id {
|
||||||
let archetype = &mut world.archetypes[archetype_id];
|
let archetype = &mut world.archetypes[archetype_id];
|
||||||
// SAFETY: The edge is assured to be initialized when we called add_bundle_to_archetype
|
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
|
||||||
let add_bundle = unsafe {
|
let archetype_after_insert = unsafe {
|
||||||
archetype
|
archetype
|
||||||
.edges()
|
.edges()
|
||||||
.get_add_bundle_internal(bundle_id)
|
.get_archetype_after_bundle_insert_internal(bundle_id)
|
||||||
.debug_checked_unwrap()
|
.debug_checked_unwrap()
|
||||||
};
|
};
|
||||||
let table_id = archetype.table_id();
|
let table_id = archetype.table_id();
|
||||||
let table = &mut world.storages.tables[table_id];
|
let table = &mut world.storages.tables[table_id];
|
||||||
Self {
|
Self {
|
||||||
add_bundle: add_bundle.into(),
|
archetype_after_insert: archetype_after_insert.into(),
|
||||||
archetype: archetype.into(),
|
archetype: archetype.into(),
|
||||||
bundle_info: bundle_info.into(),
|
bundle_info: bundle_info.into(),
|
||||||
table: table.into(),
|
table: table.into(),
|
||||||
result: InsertBundleResult::SameArchetype,
|
archetype_move_type: ArchetypeMoveType::SameArchetype,
|
||||||
change_tick,
|
change_tick,
|
||||||
world: world.as_unsafe_world_cell(),
|
world: world.as_unsafe_world_cell(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (archetype, new_archetype) =
|
let (archetype, new_archetype) =
|
||||||
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
|
world.archetypes.get_2_mut(archetype_id, new_archetype_id);
|
||||||
// SAFETY: The edge is assured to be initialized when we called add_bundle_to_archetype
|
// SAFETY: The edge is assured to be initialized when we called insert_bundle_into_archetype
|
||||||
let add_bundle = unsafe {
|
let archetype_after_insert = unsafe {
|
||||||
archetype
|
archetype
|
||||||
.edges()
|
.edges()
|
||||||
.get_add_bundle_internal(bundle_id)
|
.get_archetype_after_bundle_insert_internal(bundle_id)
|
||||||
.debug_checked_unwrap()
|
.debug_checked_unwrap()
|
||||||
};
|
};
|
||||||
let table_id = archetype.table_id();
|
let table_id = archetype.table_id();
|
||||||
@ -851,11 +980,11 @@ impl<'w> BundleInserter<'w> {
|
|||||||
if table_id == new_table_id {
|
if table_id == new_table_id {
|
||||||
let table = &mut world.storages.tables[table_id];
|
let table = &mut world.storages.tables[table_id];
|
||||||
Self {
|
Self {
|
||||||
add_bundle: add_bundle.into(),
|
archetype_after_insert: archetype_after_insert.into(),
|
||||||
archetype: archetype.into(),
|
archetype: archetype.into(),
|
||||||
bundle_info: bundle_info.into(),
|
bundle_info: bundle_info.into(),
|
||||||
table: table.into(),
|
table: table.into(),
|
||||||
result: InsertBundleResult::NewArchetypeSameTable {
|
archetype_move_type: ArchetypeMoveType::NewArchetypeSameTable {
|
||||||
new_archetype: new_archetype.into(),
|
new_archetype: new_archetype.into(),
|
||||||
},
|
},
|
||||||
change_tick,
|
change_tick,
|
||||||
@ -864,11 +993,11 @@ impl<'w> BundleInserter<'w> {
|
|||||||
} else {
|
} else {
|
||||||
let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id);
|
let (table, new_table) = world.storages.tables.get_2_mut(table_id, new_table_id);
|
||||||
Self {
|
Self {
|
||||||
add_bundle: add_bundle.into(),
|
archetype_after_insert: archetype_after_insert.into(),
|
||||||
archetype: archetype.into(),
|
archetype: archetype.into(),
|
||||||
bundle_info: bundle_info.into(),
|
bundle_info: bundle_info.into(),
|
||||||
table: table.into(),
|
table: table.into(),
|
||||||
result: InsertBundleResult::NewArchetypeNewTable {
|
archetype_move_type: ArchetypeMoveType::NewArchetypeNewTable {
|
||||||
new_archetype: new_archetype.into(),
|
new_archetype: new_archetype.into(),
|
||||||
new_table: new_table.into(),
|
new_table: new_table.into(),
|
||||||
},
|
},
|
||||||
@ -892,7 +1021,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
|
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
|
||||||
) -> EntityLocation {
|
) -> EntityLocation {
|
||||||
let bundle_info = self.bundle_info.as_ref();
|
let bundle_info = self.bundle_info.as_ref();
|
||||||
let add_bundle = self.add_bundle.as_ref();
|
let archetype_after_insert = self.archetype_after_insert.as_ref();
|
||||||
let table = self.table.as_mut();
|
let table = self.table.as_mut();
|
||||||
let archetype = self.archetype.as_ref();
|
let archetype = self.archetype.as_ref();
|
||||||
|
|
||||||
@ -907,10 +1036,14 @@ impl<'w> BundleInserter<'w> {
|
|||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_REPLACE,
|
ON_REPLACE,
|
||||||
entity,
|
entity,
|
||||||
add_bundle.iter_existing(),
|
archetype_after_insert.iter_existing(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
deferred_world.trigger_on_replace(archetype, entity, add_bundle.iter_existing());
|
deferred_world.trigger_on_replace(
|
||||||
|
archetype,
|
||||||
|
entity,
|
||||||
|
archetype_after_insert.iter_existing(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,8 +1051,8 @@ impl<'w> BundleInserter<'w> {
|
|||||||
// so this reference can only be promoted from shared to &mut down here, after they have been ran
|
// so this reference can only be promoted from shared to &mut down here, after they have been ran
|
||||||
let archetype = self.archetype.as_mut();
|
let archetype = self.archetype.as_mut();
|
||||||
|
|
||||||
let (new_archetype, new_location) = match &mut self.result {
|
let (new_archetype, new_location) = match &mut self.archetype_move_type {
|
||||||
InsertBundleResult::SameArchetype => {
|
ArchetypeMoveType::SameArchetype => {
|
||||||
// SAFETY: Mutable references do not alias and will be dropped after this block
|
// SAFETY: Mutable references do not alias and will be dropped after this block
|
||||||
let sparse_sets = {
|
let sparse_sets = {
|
||||||
let world = self.world.world_mut();
|
let world = self.world.world_mut();
|
||||||
@ -929,8 +1062,8 @@ impl<'w> BundleInserter<'w> {
|
|||||||
bundle_info.write_components(
|
bundle_info.write_components(
|
||||||
table,
|
table,
|
||||||
sparse_sets,
|
sparse_sets,
|
||||||
add_bundle,
|
archetype_after_insert,
|
||||||
add_bundle.required_components.iter(),
|
archetype_after_insert.required_components.iter(),
|
||||||
entity,
|
entity,
|
||||||
location.table_row,
|
location.table_row,
|
||||||
self.change_tick,
|
self.change_tick,
|
||||||
@ -942,7 +1075,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
|
|
||||||
(archetype, location)
|
(archetype, location)
|
||||||
}
|
}
|
||||||
InsertBundleResult::NewArchetypeSameTable { new_archetype } => {
|
ArchetypeMoveType::NewArchetypeSameTable { new_archetype } => {
|
||||||
let new_archetype = new_archetype.as_mut();
|
let new_archetype = new_archetype.as_mut();
|
||||||
|
|
||||||
// SAFETY: Mutable references do not alias and will be dropped after this block
|
// SAFETY: Mutable references do not alias and will be dropped after this block
|
||||||
@ -971,8 +1104,8 @@ impl<'w> BundleInserter<'w> {
|
|||||||
bundle_info.write_components(
|
bundle_info.write_components(
|
||||||
table,
|
table,
|
||||||
sparse_sets,
|
sparse_sets,
|
||||||
add_bundle,
|
archetype_after_insert,
|
||||||
add_bundle.required_components.iter(),
|
archetype_after_insert.required_components.iter(),
|
||||||
entity,
|
entity,
|
||||||
result.table_row,
|
result.table_row,
|
||||||
self.change_tick,
|
self.change_tick,
|
||||||
@ -984,7 +1117,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
|
|
||||||
(new_archetype, new_location)
|
(new_archetype, new_location)
|
||||||
}
|
}
|
||||||
InsertBundleResult::NewArchetypeNewTable {
|
ArchetypeMoveType::NewArchetypeNewTable {
|
||||||
new_archetype,
|
new_archetype,
|
||||||
new_table,
|
new_table,
|
||||||
} => {
|
} => {
|
||||||
@ -1022,7 +1155,7 @@ impl<'w> BundleInserter<'w> {
|
|||||||
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
||||||
entities.set(entity.index(), new_location);
|
entities.set(entity.index(), new_location);
|
||||||
|
|
||||||
// if an entity was moved into this entity's table spot, update its table row
|
// If an entity was moved into this entity's table spot, update its table row.
|
||||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||||
let swapped_location =
|
let swapped_location =
|
||||||
// SAFETY: If the swap was successful, swapped_entity must be valid.
|
// SAFETY: If the swap was successful, swapped_entity must be valid.
|
||||||
@ -1054,8 +1187,8 @@ impl<'w> BundleInserter<'w> {
|
|||||||
bundle_info.write_components(
|
bundle_info.write_components(
|
||||||
new_table,
|
new_table,
|
||||||
sparse_sets,
|
sparse_sets,
|
||||||
add_bundle,
|
archetype_after_insert,
|
||||||
add_bundle.required_components.iter(),
|
archetype_after_insert.required_components.iter(),
|
||||||
entity,
|
entity,
|
||||||
move_result.new_row,
|
move_result.new_row,
|
||||||
self.change_tick,
|
self.change_tick,
|
||||||
@ -1076,39 +1209,47 @@ impl<'w> BundleInserter<'w> {
|
|||||||
// SAFETY: All components in the bundle are guaranteed to exist in the World
|
// SAFETY: All components in the bundle are guaranteed to exist in the World
|
||||||
// as they must be initialized before creating the BundleInfo.
|
// as they must be initialized before creating the BundleInfo.
|
||||||
unsafe {
|
unsafe {
|
||||||
deferred_world.trigger_on_add(new_archetype, entity, add_bundle.iter_added());
|
deferred_world.trigger_on_add(
|
||||||
|
new_archetype,
|
||||||
|
entity,
|
||||||
|
archetype_after_insert.iter_added(),
|
||||||
|
);
|
||||||
if new_archetype.has_add_observer() {
|
if new_archetype.has_add_observer() {
|
||||||
deferred_world.trigger_observers(ON_ADD, entity, add_bundle.iter_added());
|
deferred_world.trigger_observers(
|
||||||
|
ON_ADD,
|
||||||
|
entity,
|
||||||
|
archetype_after_insert.iter_added(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
match insert_mode {
|
match insert_mode {
|
||||||
InsertMode::Replace => {
|
InsertMode::Replace => {
|
||||||
// insert triggers for both new and existing components if we're replacing them
|
// Insert triggers for both new and existing components if we're replacing them.
|
||||||
deferred_world.trigger_on_insert(
|
deferred_world.trigger_on_insert(
|
||||||
new_archetype,
|
new_archetype,
|
||||||
entity,
|
entity,
|
||||||
add_bundle.iter_inserted(),
|
archetype_after_insert.iter_inserted(),
|
||||||
);
|
);
|
||||||
if new_archetype.has_insert_observer() {
|
if new_archetype.has_insert_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
entity,
|
||||||
add_bundle.iter_inserted(),
|
archetype_after_insert.iter_inserted(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertMode::Keep => {
|
InsertMode::Keep => {
|
||||||
// insert triggers only for new components if we're not replacing them (since
|
// Insert triggers only for new components if we're not replacing them (since
|
||||||
// nothing is actually inserted).
|
// nothing is actually inserted).
|
||||||
deferred_world.trigger_on_insert(
|
deferred_world.trigger_on_insert(
|
||||||
new_archetype,
|
new_archetype,
|
||||||
entity,
|
entity,
|
||||||
add_bundle.iter_added(),
|
archetype_after_insert.iter_added(),
|
||||||
);
|
);
|
||||||
if new_archetype.has_insert_observer() {
|
if new_archetype.has_insert_observer() {
|
||||||
deferred_world.trigger_observers(
|
deferred_world.trigger_observers(
|
||||||
ON_INSERT,
|
ON_INSERT,
|
||||||
entity,
|
entity,
|
||||||
add_bundle.iter_added(),
|
archetype_after_insert.iter_added(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1155,7 +1296,7 @@ impl<'w> BundleSpawner<'w> {
|
|||||||
change_tick: Tick,
|
change_tick: Tick,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let bundle_info = world.bundles.get_unchecked(bundle_id);
|
let bundle_info = world.bundles.get_unchecked(bundle_id);
|
||||||
let new_archetype_id = bundle_info.add_bundle_to_archetype(
|
let new_archetype_id = bundle_info.insert_bundle_into_archetype(
|
||||||
&mut world.archetypes,
|
&mut world.archetypes,
|
||||||
&mut world.storages,
|
&mut world.storages,
|
||||||
&world.components,
|
&world.components,
|
||||||
@ -1482,6 +1623,21 @@ fn initialize_dynamic_bundle(
|
|||||||
(id, storage_types)
|
(id, storage_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
|
||||||
|
let mut remove_index = 0;
|
||||||
|
source.retain(|value| {
|
||||||
|
while remove_index < remove.len() && *value > remove[remove_index] {
|
||||||
|
remove_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if remove_index < remove.len() {
|
||||||
|
*value != remove[remove_index]
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate as bevy_ecs;
|
use crate as bevy_ecs;
|
||||||
@ -1678,4 +1834,25 @@ mod tests {
|
|||||||
assert!(entity.contains::<A>());
|
assert!(entity.contains::<A>());
|
||||||
assert_eq!(entity.get(), Some(&V("one")));
|
assert_eq!(entity.get(), Some(&V("one")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sorted_remove() {
|
||||||
|
let mut a = vec![1, 2, 3, 4, 5, 6, 7];
|
||||||
|
let b = vec![1, 2, 3, 5, 7];
|
||||||
|
super::sorted_remove(&mut a, &b);
|
||||||
|
|
||||||
|
assert_eq!(a, vec![4, 6]);
|
||||||
|
|
||||||
|
let mut a = vec![1];
|
||||||
|
let b = vec![1];
|
||||||
|
super::sorted_remove(&mut a, &b);
|
||||||
|
|
||||||
|
assert_eq!(a, vec![]);
|
||||||
|
|
||||||
|
let mut a = vec![1];
|
||||||
|
let b = vec![2];
|
||||||
|
super::sorted_remove(&mut a, &b);
|
||||||
|
|
||||||
|
assert_eq!(a, vec![1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
||||||
entity::{Entities, Entity, EntityLocation},
|
entity::{Entities, Entity, EntityLocation},
|
||||||
event::Event,
|
event::Event,
|
||||||
observer::{Observer, Observers},
|
observer::Observer,
|
||||||
query::{Access, ReadOnlyQueryData},
|
query::{Access, ReadOnlyQueryData},
|
||||||
removal_detection::RemovedComponentEvents,
|
removal_detection::RemovedComponentEvents,
|
||||||
storage::Storages,
|
storage::Storages,
|
||||||
@ -1468,13 +1468,12 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
// SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid,
|
// SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid,
|
||||||
// components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T`
|
// components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T`
|
||||||
let new_archetype_id = unsafe {
|
let new_archetype_id = unsafe {
|
||||||
remove_bundle_from_archetype(
|
bundle_info.remove_bundle_from_archetype(
|
||||||
&mut world.archetypes,
|
&mut world.archetypes,
|
||||||
storages,
|
storages,
|
||||||
components,
|
components,
|
||||||
&world.observers,
|
&world.observers,
|
||||||
old_location.archetype_id,
|
old_location.archetype_id,
|
||||||
bundle_info,
|
|
||||||
false,
|
false,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
@ -1650,13 +1649,13 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: `archetype_id` exists because it is referenced in `location` which is valid
|
// SAFETY: `archetype_id` exists because it is referenced in `location` which is valid
|
||||||
// and components in `bundle_info` must exist due to this function's safety invariants.
|
// and components in `bundle_info` must exist due to this function's safety invariants.
|
||||||
let new_archetype_id = remove_bundle_from_archetype(
|
let new_archetype_id = bundle_info
|
||||||
|
.remove_bundle_from_archetype(
|
||||||
&mut world.archetypes,
|
&mut world.archetypes,
|
||||||
&mut world.storages,
|
&mut world.storages,
|
||||||
&world.components,
|
&world.components,
|
||||||
&world.observers,
|
&world.observers,
|
||||||
location.archetype_id,
|
location.archetype_id,
|
||||||
bundle_info,
|
|
||||||
// components from the bundle that are not present on the entity are ignored
|
// components from the bundle that are not present on the entity are ignored
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@ -3271,126 +3270,6 @@ unsafe fn insert_dynamic_bundle<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a bundle from the given archetype and returns the resulting archetype (or None if the
|
|
||||||
/// removal was invalid). in the event that adding the given bundle does not result in an Archetype
|
|
||||||
/// change. Results are cached in the Archetype Graph to avoid redundant work.
|
|
||||||
/// if `intersection` is false, attempting to remove a bundle with components _not_ contained in the
|
|
||||||
/// current archetype will fail, returning None. if `intersection` is true, components in the bundle
|
|
||||||
/// but not in the current archetype will be ignored
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// `archetype_id` must exist and components in `bundle_info` must exist
|
|
||||||
unsafe fn remove_bundle_from_archetype(
|
|
||||||
archetypes: &mut Archetypes,
|
|
||||||
storages: &mut Storages,
|
|
||||||
components: &Components,
|
|
||||||
observers: &Observers,
|
|
||||||
archetype_id: ArchetypeId,
|
|
||||||
bundle_info: &BundleInfo,
|
|
||||||
intersection: bool,
|
|
||||||
) -> Option<ArchetypeId> {
|
|
||||||
// check the archetype graph to see if the Bundle has been removed from this archetype in the
|
|
||||||
// past
|
|
||||||
let remove_bundle_result = {
|
|
||||||
let edges = archetypes[archetype_id].edges();
|
|
||||||
if intersection {
|
|
||||||
edges.get_remove_bundle(bundle_info.id())
|
|
||||||
} else {
|
|
||||||
edges.get_take_bundle(bundle_info.id())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let result = if let Some(result) = remove_bundle_result {
|
|
||||||
// this Bundle removal result is cached. just return that!
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
let mut next_table_components;
|
|
||||||
let mut next_sparse_set_components;
|
|
||||||
let next_table_id;
|
|
||||||
{
|
|
||||||
let current_archetype = &mut archetypes[archetype_id];
|
|
||||||
let mut removed_table_components = Vec::new();
|
|
||||||
let mut removed_sparse_set_components = Vec::new();
|
|
||||||
for component_id in bundle_info.iter_explicit_components() {
|
|
||||||
if current_archetype.contains(component_id) {
|
|
||||||
// SAFETY: bundle components were already initialized by bundles.get_info
|
|
||||||
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
|
||||||
match component_info.storage_type() {
|
|
||||||
StorageType::Table => removed_table_components.push(component_id),
|
|
||||||
StorageType::SparseSet => removed_sparse_set_components.push(component_id),
|
|
||||||
}
|
|
||||||
} else if !intersection {
|
|
||||||
// a component in the bundle was not present in the entity's archetype, so this
|
|
||||||
// removal is invalid cache the result in the archetype
|
|
||||||
// graph
|
|
||||||
current_archetype
|
|
||||||
.edges_mut()
|
|
||||||
.insert_take_bundle(bundle_info.id(), None);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort removed components so we can do an efficient "sorted remove". archetype
|
|
||||||
// components are already sorted
|
|
||||||
removed_table_components.sort_unstable();
|
|
||||||
removed_sparse_set_components.sort_unstable();
|
|
||||||
next_table_components = current_archetype.table_components().collect();
|
|
||||||
next_sparse_set_components = current_archetype.sparse_set_components().collect();
|
|
||||||
sorted_remove(&mut next_table_components, &removed_table_components);
|
|
||||||
sorted_remove(
|
|
||||||
&mut next_sparse_set_components,
|
|
||||||
&removed_sparse_set_components,
|
|
||||||
);
|
|
||||||
|
|
||||||
next_table_id = if removed_table_components.is_empty() {
|
|
||||||
current_archetype.table_id()
|
|
||||||
} else {
|
|
||||||
// SAFETY: all components in next_table_components exist
|
|
||||||
unsafe {
|
|
||||||
storages
|
|
||||||
.tables
|
|
||||||
.get_id_or_insert(&next_table_components, components)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_archetype_id = archetypes.get_id_or_insert(
|
|
||||||
components,
|
|
||||||
observers,
|
|
||||||
next_table_id,
|
|
||||||
next_table_components,
|
|
||||||
next_sparse_set_components,
|
|
||||||
);
|
|
||||||
Some(new_archetype_id)
|
|
||||||
};
|
|
||||||
let current_archetype = &mut archetypes[archetype_id];
|
|
||||||
// cache the result in an edge
|
|
||||||
if intersection {
|
|
||||||
current_archetype
|
|
||||||
.edges_mut()
|
|
||||||
.insert_remove_bundle(bundle_info.id(), result);
|
|
||||||
} else {
|
|
||||||
current_archetype
|
|
||||||
.edges_mut()
|
|
||||||
.insert_take_bundle(bundle_info.id(), result);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
|
|
||||||
let mut remove_index = 0;
|
|
||||||
source.retain(|value| {
|
|
||||||
while remove_index < remove.len() && *value > remove[remove_index] {
|
|
||||||
remove_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if remove_index < remove.len() {
|
|
||||||
*value != remove[remove_index]
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Moves component data out of storage.
|
/// Moves component data out of storage.
|
||||||
///
|
///
|
||||||
/// This function leaves the underlying memory unchanged, but the component behind
|
/// This function leaves the underlying memory unchanged, but the component behind
|
||||||
@ -3700,27 +3579,6 @@ mod tests {
|
|||||||
|
|
||||||
use super::{EntityMutExcept, EntityRefExcept};
|
use super::{EntityMutExcept, EntityRefExcept};
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sorted_remove() {
|
|
||||||
let mut a = vec![1, 2, 3, 4, 5, 6, 7];
|
|
||||||
let b = vec![1, 2, 3, 5, 7];
|
|
||||||
super::sorted_remove(&mut a, &b);
|
|
||||||
|
|
||||||
assert_eq!(a, vec![4, 6]);
|
|
||||||
|
|
||||||
let mut a = vec![1];
|
|
||||||
let b = vec![1];
|
|
||||||
super::sorted_remove(&mut a, &b);
|
|
||||||
|
|
||||||
assert_eq!(a, vec![]);
|
|
||||||
|
|
||||||
let mut a = vec![1];
|
|
||||||
let b = vec![2];
|
|
||||||
super::sorted_remove(&mut a, &b);
|
|
||||||
|
|
||||||
assert_eq!(a, vec![1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Component, Clone, Copy, Debug, PartialEq)]
|
#[derive(Component, Clone, Copy, Debug, PartialEq)]
|
||||||
struct TestComponent(u32);
|
struct TestComponent(u32);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user