remove unsafe get_unchecked (and mut variant) from Tables and Archetypes (#1614)

Removes `get_unchecked` and `get_unchecked_mut` from `Tables` and `Archetypes` collections in favor of safe Index implementations. This fixes a safety error in `Archetypes::get_id_or_insert()` (which previously relied on TableId being valid to be safe ... the alternative was to make that method unsafe too). It also cuts down on a lot of unsafe and makes the code easier to look at. I'm not sure what changed since the last benchmark, but these numbers are more favorable than my last tests of similar changes. I didn't include the Components collection as those severely killed perf last time I tried. But this does inspire me to try again (just in a separate pr)! 

Note that the `simple_insert/bevy_unbatched` benchmark fluctuates a lot on both branches (this was also true for prior versions of bevy). It seems like the allocator has more variance for many small allocations. And `sparse_frag_iter/bevy` operates on such a small scale that 10% fluctuations are common.

Some benches do take a small hit here, but I personally think its worth it.

This also fixes a safety error in Query::for_each_mut, which needed to mutably borrow Query (aaahh!).  

![image](https://user-images.githubusercontent.com/2694663/110726926-2b52eb80-81cf-11eb-9ea3-bff951060c7c.png)
![image](https://user-images.githubusercontent.com/2694663/110726991-4c1b4100-81cf-11eb-9199-ca79bef0b9bd.png)
This commit is contained in:
Carter Anderson 2021-03-11 18:38:22 +00:00
parent b17f8a4bce
commit 68606934e3
10 changed files with 112 additions and 182 deletions

View file

@ -4,7 +4,12 @@ use crate::{
entity::{Entity, EntityLocation},
storage::{Column, SparseArray, SparseSet, SparseSetIndex, TableId},
};
use std::{borrow::Cow, collections::HashMap, hash::Hash};
use std::{
borrow::Cow,
collections::HashMap,
hash::Hash,
ops::{Index, IndexMut},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ArchetypeId(usize);
@ -443,27 +448,11 @@ impl Archetypes {
self.archetypes.get(id.index())
}
/// # Safety
/// `id` must be valid
#[inline]
pub unsafe fn get_unchecked(&self, id: ArchetypeId) -> &Archetype {
debug_assert!(id.index() < self.archetypes.len());
self.archetypes.get_unchecked(id.index())
}
#[inline]
pub fn get_mut(&mut self, id: ArchetypeId) -> Option<&mut Archetype> {
self.archetypes.get_mut(id.index())
}
/// # Safety
/// `id` must be valid
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, id: ArchetypeId) -> &mut Archetype {
debug_assert!(id.index() < self.archetypes.len());
self.archetypes.get_unchecked_mut(id.index())
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &Archetype> {
self.archetypes.iter()
@ -522,3 +511,19 @@ impl Archetypes {
self.archetype_component_count
}
}
impl Index<ArchetypeId> for Archetypes {
type Output = Archetype;
#[inline]
fn index(&self, index: ArchetypeId) -> &Self::Output {
&self.archetypes[index.index()]
}
}
impl IndexMut<ArchetypeId> for Archetypes {
#[inline]
fn index_mut(&mut self, index: ArchetypeId) -> &mut Self::Output {
&mut self.archetypes[index.index()]
}
}

View file

@ -271,9 +271,9 @@ impl<'w, T: Component> Fetch<'w> for ReadFetch<T> {
match state.storage_type {
StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
// SAFE: archetype tables always exist
let table = tables.get_unchecked(archetype.table_id());
let column = table.get_column(state.component_id).unwrap();
let column = tables[archetype.table_id()]
.get_column(state.component_id)
.unwrap();
self.table_components = column.get_ptr().cast::<T>();
}
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),
@ -411,9 +411,9 @@ impl<'w, T: Component> Fetch<'w> for WriteFetch<T> {
match state.storage_type {
StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
// SAFE: archetype tables always exist
let table = tables.get_unchecked(archetype.table_id());
let column = table.get_column(state.component_id).unwrap();
let column = tables[archetype.table_id()]
.get_column(state.component_id)
.unwrap();
self.table_components = column.get_ptr().cast::<T>();
self.table_flags = column.get_flags_mut_ptr();
}
@ -698,9 +698,9 @@ impl<'w, T: Component> Fetch<'w> for FlagsFetch<T> {
match state.storage_type {
StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
// SAFE: archetype tables always exist
let table = tables.get_unchecked(archetype.table_id());
let column = table.get_column(state.component_id).unwrap();
let column = tables[archetype.table_id()]
.get_column(state.component_id)
.unwrap();
self.table_flags = column.get_flags_mut_ptr().cast::<ComponentFlags>();
}
StorageType::SparseSet => self.entities = archetype.entities().as_ptr(),

View file

@ -546,8 +546,7 @@ macro_rules! impl_flag_filter {
match state.storage_type {
StorageType::Table => {
self.entity_table_rows = archetype.entity_table_rows().as_ptr();
// SAFE: archetype tables always exist
let table = tables.get_unchecked(archetype.table_id());
let table = &tables[archetype.table_id()];
self.table_flags = table
.get_column(state.component_id).unwrap()
.get_flags_mut_ptr();

View file

@ -58,7 +58,7 @@ where
loop {
if self.current_index == self.current_len {
let table_id = self.table_id_iter.next()?;
let table = self.tables.get_unchecked(*table_id);
let table = &self.tables[*table_id];
self.fetch.set_table(&self.query_state.fetch_state, table);
self.filter.set_table(&self.query_state.filter_state, table);
self.current_len = table.len();
@ -80,7 +80,7 @@ where
loop {
if self.current_index == self.current_len {
let archetype_id = self.archetype_id_iter.next()?;
let archetype = self.archetypes.get_unchecked(*archetype_id);
let archetype = &self.archetypes[*archetype_id];
self.fetch.set_archetype(
&self.query_state.fetch_state,
archetype,
@ -120,12 +120,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> {
self.query_state
.matched_archetypes
.ones()
.map(|index| {
// SAFE: matched archetypes always exist
let archetype =
unsafe { self.world.archetypes.get_unchecked(ArchetypeId::new(index)) };
archetype.len()
})
.map(|index| self.world.archetypes[ArchetypeId::new(index)].len())
.sum()
}
}

View file

@ -75,9 +75,7 @@ where
}
};
for archetype_index in archetype_index_range {
// SAFE: archetype indices less than the archetype generation are guaranteed to exist
let archetype = unsafe { archetypes.get_unchecked(ArchetypeId::new(archetype_index)) };
self.new_archetype(archetype);
self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]);
}
}
@ -155,8 +153,7 @@ where
{
return Err(QueryEntityError::QueryDoesNotMatch);
}
// SAFE: live entities always exist in an archetype
let archetype = world.archetypes.get_unchecked(location.archetype_id);
let archetype = &world.archetypes[location.archetype_id];
let mut fetch = <Q::Fetch as Fetch>::init(world, &self.fetch_state);
let mut filter = <F::Fetch as Fetch>::init(world, &self.filter_state);
@ -308,7 +305,7 @@ where
if fetch.is_dense() && filter.is_dense() {
let tables = &world.storages().tables;
for table_id in self.matched_table_ids.iter() {
let table = tables.get_unchecked(*table_id);
let table = &tables[*table_id];
fetch.set_table(&self.fetch_state, table);
filter.set_table(&self.filter_state, table);
@ -324,7 +321,7 @@ where
let archetypes = &world.archetypes;
let tables = &world.storages().tables;
for archetype_id in self.matched_archetype_ids.iter() {
let archetype = archetypes.get_unchecked(*archetype_id);
let archetype = &archetypes[*archetype_id];
fetch.set_archetype(&self.fetch_state, archetype, tables);
filter.set_archetype(&self.filter_state, archetype, tables);
@ -357,7 +354,7 @@ where
if fetch.is_dense() && filter.is_dense() {
let tables = &world.storages().tables;
for table_id in self.matched_table_ids.iter() {
let table = tables.get_unchecked(*table_id);
let table = &tables[*table_id];
let mut offset = 0;
while offset < table.len() {
let func = func.clone();
@ -365,7 +362,7 @@ where
let mut fetch = <Q::Fetch as Fetch>::init(world, &self.fetch_state);
let mut filter = <F::Fetch as Fetch>::init(world, &self.filter_state);
let tables = &world.storages().tables;
let table = tables.get_unchecked(*table_id);
let table = &tables[*table_id];
fetch.set_table(&self.fetch_state, table);
filter.set_table(&self.filter_state, table);
let len = batch_size.min(table.len() - offset);
@ -384,14 +381,14 @@ where
let archetypes = &world.archetypes;
for archetype_id in self.matched_archetype_ids.iter() {
let mut offset = 0;
let archetype = archetypes.get_unchecked(*archetype_id);
let archetype = &archetypes[*archetype_id];
while offset < archetype.len() {
let func = func.clone();
scope.spawn(async move {
let mut fetch = <Q::Fetch as Fetch>::init(world, &self.fetch_state);
let mut filter = <F::Fetch as Fetch>::init(world, &self.filter_state);
let tables = &world.storages().tables;
let archetype = world.archetypes.get_unchecked(*archetype_id);
let archetype = &world.archetypes[*archetype_id];
fetch.set_archetype(&self.fetch_state, archetype, tables);
filter.set_archetype(&self.filter_state, archetype, tables);

View file

@ -8,6 +8,7 @@ use bevy_utils::{AHasher, HashMap};
use std::{
cell::UnsafeCell,
hash::{Hash, Hasher},
ops::{Index, IndexMut},
ptr::NonNull,
};
@ -407,30 +408,14 @@ impl Tables {
self.tables.is_empty()
}
#[inline]
pub fn get_mut(&mut self, id: TableId) -> Option<&mut Table> {
self.tables.get_mut(id.index())
}
#[inline]
pub fn get(&self, id: TableId) -> Option<&Table> {
self.tables.get(id.index())
}
/// # Safety
/// `id` must be a valid table
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, id: TableId) -> &mut Table {
debug_assert!(id.index() < self.tables.len());
self.tables.get_unchecked_mut(id.index())
}
/// # Safety
/// `id` must be a valid table
#[inline]
pub unsafe fn get_unchecked(&self, id: TableId) -> &Table {
debug_assert!(id.index() < self.tables.len());
self.tables.get_unchecked(id.index())
pub fn get_mut(&mut self, id: TableId) -> Option<&mut Table> {
self.tables.get_mut(id.index())
}
#[inline]
@ -476,6 +461,22 @@ impl Tables {
}
}
impl Index<TableId> for Tables {
type Output = Table;
#[inline]
fn index(&self, index: TableId) -> &Self::Output {
&self.tables[index.index()]
}
}
impl IndexMut<TableId> for Tables {
#[inline]
fn index_mut(&mut self, index: TableId) -> &mut Self::Output {
&mut self.tables[index.index()]
}
}
#[cfg(test)]
mod tests {
use crate::{

View file

@ -77,7 +77,7 @@ where
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// be chained like a normal iterator.
#[inline]
pub fn for_each_mut(&self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) {
pub fn for_each_mut(&mut self, f: impl FnMut(<Q::Fetch as Fetch<'w>>::Item)) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe { self.state.for_each_unchecked_manual(self.world, f) };

View file

@ -36,13 +36,7 @@ impl<'w> EntityRef<'w> {
#[inline]
pub fn archetype(&self) -> &Archetype {
// SAFE: EntityRefs always point to valid entities. Valid entities always have valid
// archetypes
unsafe {
self.world
.archetypes
.get_unchecked(self.location.archetype_id)
}
&self.world.archetypes[self.location.archetype_id]
}
#[inline]
@ -57,14 +51,12 @@ impl<'w> EntityRef<'w> {
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
// SAFE: entity location is valid
unsafe { contains_component_with_id(self.world, component_id, self.location) }
contains_component_with_id(self.world, component_id, self.location)
}
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
// SAFE: entity location is valid
unsafe { contains_component_with_type(self.world, type_id, self.location) }
contains_component_with_type(self.world, type_id, self.location)
}
#[inline]
@ -123,13 +115,7 @@ impl<'w> EntityMut<'w> {
#[inline]
pub fn archetype(&self) -> &Archetype {
// SAFE: EntityRefs always point to valid entities. Valid entities always have valid
// archetypes
unsafe {
self.world
.archetypes
.get_unchecked(self.location.archetype_id)
}
&self.world.archetypes[self.location.archetype_id]
}
#[inline]
@ -139,14 +125,12 @@ impl<'w> EntityMut<'w> {
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
// SAFE: entity location is valid
unsafe { contains_component_with_id(self.world, component_id, self.location) }
contains_component_with_id(self.world, component_id, self.location)
}
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
// SAFE: entity location is valid
unsafe { contains_component_with_type(self.world, type_id, self.location) }
contains_component_with_type(self.world, type_id, self.location)
}
#[inline]
@ -215,7 +199,7 @@ impl<'w> EntityMut<'w> {
let old_table_row;
let old_table_id;
{
let old_archetype = archetypes.get_unchecked_mut(current_location.archetype_id);
let old_archetype = &mut archetypes[current_location.archetype_id];
let result = old_archetype.swap_remove(current_location.index);
if let Some(swapped_entity) = result.swapped_entity {
// SAFE: entity is live and is contained in an archetype that exists
@ -224,7 +208,7 @@ impl<'w> EntityMut<'w> {
old_table_row = result.table_row;
old_table_id = old_archetype.table_id()
}
let new_archetype = archetypes.get_unchecked_mut(new_archetype_id);
let new_archetype = &mut archetypes[new_archetype_id];
if old_table_id == new_archetype.table_id() {
new_archetype.allocate(entity, old_table_row)
@ -243,8 +227,7 @@ impl<'w> EntityMut<'w> {
let swapped_location = entities.get(swapped_entity).unwrap();
// SAFE: entity is live and is therefore contained in an archetype that
// exists
archetypes
.get_unchecked_mut(swapped_location.archetype_id)
archetypes[swapped_location.archetype_id]
.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
@ -256,10 +239,8 @@ impl<'w> EntityMut<'w> {
self.location = new_location;
entities.meta[self.entity.id as usize].location = new_location;
// SAFE: archetype was created if it didn't already exist
let archetype = unsafe { archetypes.get_unchecked_mut(new_location.archetype_id) };
// SAFE: archetype tables always exists
let table = unsafe { storages.tables.get_unchecked_mut(archetype.table_id()) };
let archetype = &archetypes[new_location.archetype_id];
let table = &storages.tables[archetype.table_id()];
let table_row = archetype.entity_table_row(new_location.index);
let from_bundle = archetype.edges().get_from_bundle(bundle_info.id).unwrap();
// SAFE: table row is valid
@ -300,8 +281,7 @@ impl<'w> EntityMut<'w> {
return None;
}
// SAFE: current entity archetype is valid
let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) };
let old_archetype = &mut archetypes[old_location.archetype_id];
let mut bundle_components = bundle_info.component_ids.iter().cloned();
let entity = self.entity;
// SAFE: bundle components are iterated in order, which guarantees that the component type
@ -328,8 +308,7 @@ impl<'w> EntityMut<'w> {
}
let old_table_row = remove_result.table_row;
let old_table_id = old_archetype.table_id();
// SAFE: new archetype exists thanks to remove_bundle_from_archetype
let new_archetype = unsafe { archetypes.get_unchecked_mut(new_archetype_id) };
let new_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_id() {
unsafe { new_archetype.allocate(entity, old_table_row) }
@ -349,9 +328,7 @@ impl<'w> EntityMut<'w> {
// if an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
// SAFE: entity is live and is contained in an archetype that exists
let archetype =
unsafe { archetypes.get_unchecked_mut(swapped_location.archetype_id) };
let archetype = &mut archetypes[swapped_location.archetype_id];
archetype.set_entity_table_row(swapped_location.index, old_table_row);
}
@ -390,8 +367,7 @@ impl<'w> EntityMut<'w> {
return;
}
// SAFE: current entity archetype is valid
let old_archetype = unsafe { archetypes.get_unchecked_mut(old_location.archetype_id) };
let old_archetype = &mut archetypes[old_location.archetype_id];
let entity = self.entity;
for component_id in bundle_info.component_ids.iter().cloned() {
if old_archetype.contains(component_id) {
@ -416,8 +392,7 @@ impl<'w> EntityMut<'w> {
}
let old_table_row = remove_result.table_row;
let old_table_id = old_archetype.table_id();
// SAFE: new archetype exists thanks to remove_bundle_from_archetype
let new_archetype = unsafe { archetypes.get_unchecked_mut(new_archetype_id) };
let new_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_id() {
unsafe { new_archetype.allocate(entity, old_table_row) }
@ -436,10 +411,8 @@ impl<'w> EntityMut<'w> {
// if an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
// SAFE: entity is live and is contained in an archetype that exists
let archetype =
unsafe { archetypes.get_unchecked_mut(swapped_location.archetype_id) };
archetype.set_entity_table_row(swapped_location.index, old_table_row);
archetypes[swapped_location.archetype_id]
.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
@ -467,8 +440,7 @@ impl<'w> EntityMut<'w> {
let table_row;
let moved_entity;
{
// SAFE: entity is live and is contained in an archetype that exists
let archetype = unsafe { world.archetypes.get_unchecked_mut(location.archetype_id) };
let archetype = &mut world.archetypes[location.archetype_id];
for component_id in archetype.components() {
let removed_components = world
.removed_components
@ -485,25 +457,16 @@ impl<'w> EntityMut<'w> {
let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap();
sparse_set.remove(self.entity);
}
// SAFE: tables and table rows stored in archetypes always exist
// SAFE: table rows stored in archetypes always exist
moved_entity = unsafe {
world
.storages
.tables
.get_unchecked_mut(archetype.table_id())
.swap_remove_unchecked(table_row)
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
};
};
if let Some(moved_entity) = moved_entity {
let moved_location = world.entities.get(moved_entity).unwrap();
// SAFE: entity is live and is contained in an archetype that exists
let archetype = unsafe {
world
.archetypes
.get_unchecked_mut(moved_location.archetype_id)
};
archetype.set_entity_table_row(moved_location.index, table_row);
world.archetypes[moved_location.archetype_id]
.set_entity_table_row(moved_location.index, table_row);
}
}
@ -539,13 +502,12 @@ unsafe fn get_component(
entity: Entity,
location: EntityLocation,
) -> Option<*mut u8> {
let archetype = world.archetypes.get_unchecked(location.archetype_id);
let archetype = &world.archetypes[location.archetype_id];
// SAFE: component_id exists and is therefore valid
let component_info = world.components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => {
// SAFE: tables stored in archetype always exist
let table = world.storages.tables.get_unchecked(archetype.table_id());
let table = &world.storages.tables[archetype.table_id()];
let components = table.get_column(component_id)?;
let table_row = archetype.entity_table_row(location.index);
// SAFE: archetypes only store valid table_rows and the stored component type is T
@ -568,12 +530,11 @@ unsafe fn get_component_and_flags(
entity: Entity,
location: EntityLocation,
) -> Option<(*mut u8, *mut ComponentFlags)> {
let archetype = world.archetypes.get_unchecked(location.archetype_id);
let archetype = &world.archetypes[location.archetype_id];
let component_info = world.components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => {
// SAFE: tables stored in archetype always exist
let table = world.storages.tables.get_unchecked(archetype.table_id());
let table = &world.storages.tables[archetype.table_id()];
let components = table.get_column(component_id)?;
let table_row = archetype.entity_table_row(location.index);
// SAFE: archetypes only store valid table_rows and the stored component type is T
@ -610,8 +571,7 @@ unsafe fn remove_component(
removed_components.push(entity);
match component_info.storage_type() {
StorageType::Table => {
// SAFE: tables stored in archetype always exist
let table = storages.tables.get_unchecked(archetype.table_id());
let table = &storages.tables[archetype.table_id()];
// SAFE: archetypes will always point to valid columns
let components = table.get_column(component_id).unwrap();
let table_row = archetype.entity_table_row(location.index);
@ -651,13 +611,7 @@ pub(crate) unsafe fn get_component_and_flags_with_type(
get_component_and_flags(world, component_id, entity, location)
}
/// # Safety
/// `entity_location` must be within bounds of an archetype that exists.
unsafe fn contains_component_with_type(
world: &World,
type_id: TypeId,
location: EntityLocation,
) -> bool {
fn contains_component_with_type(world: &World, type_id: TypeId, location: EntityLocation) -> bool {
if let Some(component_id) = world.components.get_id(type_id) {
contains_component_with_id(world, component_id, location)
} else {
@ -665,17 +619,12 @@ unsafe fn contains_component_with_type(
}
}
/// # Safety
/// `entity_location` must be within bounds of an archetype that exists.
unsafe fn contains_component_with_id(
fn contains_component_with_id(
world: &World,
component_id: ComponentId,
location: EntityLocation,
) -> bool {
world
.archetypes
.get_unchecked(location.archetype_id)
.contains(component_id)
world.archetypes[location.archetype_id].contains(component_id)
}
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
@ -683,7 +632,7 @@ unsafe fn contains_component_with_id(
/// Results are cached in the Archetype Graph to avoid redundant work.
///
/// # Safety
/// `archetype_id` must exist and components in `bundle_info` must exist
/// components in `bundle_info` must exist
pub(crate) unsafe fn add_bundle_to_archetype(
archetypes: &mut Archetypes,
storages: &mut Storages,
@ -691,8 +640,7 @@ pub(crate) unsafe fn add_bundle_to_archetype(
archetype_id: ArchetypeId,
bundle_info: &BundleInfo,
) -> ArchetypeId {
if let Some(archetype_id) = archetypes
.get_unchecked(archetype_id)
if let Some(archetype_id) = archetypes[archetype_id]
.edges()
.get_add_bundle(bundle_info.id)
{
@ -702,7 +650,7 @@ pub(crate) unsafe fn add_bundle_to_archetype(
let mut new_sparse_set_components = Vec::new();
let mut tracking_flags = Vec::with_capacity(bundle_info.component_ids.len());
let current_archetype = archetypes.get_unchecked_mut(archetype_id);
let current_archetype = &mut archetypes[archetype_id];
for component_id in bundle_info.component_ids.iter().cloned() {
if current_archetype.contains(component_id) {
tracking_flags.push(ComponentFlags::MUTATED);
@ -731,7 +679,7 @@ pub(crate) unsafe fn add_bundle_to_archetype(
let sparse_set_components;
// the archetype changes when we add this bundle. prepare the new archetype and storages
{
let current_archetype = archetypes.get_unchecked_mut(archetype_id);
let current_archetype = &archetypes[archetype_id];
table_components = if new_table_components.is_empty() {
// if there are no new table components, we can keep using this table
table_id = current_archetype.table_id();
@ -760,15 +708,15 @@ pub(crate) unsafe fn add_bundle_to_archetype(
let new_archetype_id =
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
// add an edge from the old archetype to the new archetype
archetypes
.get_unchecked_mut(archetype_id)
archetypes[archetype_id]
.edges_mut()
.set_add_bundle(bundle_info.id, new_archetype_id);
// add a "from bundle" edge from the new archetype to the old archetype
archetypes
.get_unchecked_mut(new_archetype_id)
.edges_mut()
.set_from_bundle(bundle_info.id, new_archetype_id, tracking_flags);
archetypes[new_archetype_id].edges_mut().set_from_bundle(
bundle_info.id,
new_archetype_id,
tracking_flags,
);
new_archetype_id
}
}
@ -793,8 +741,7 @@ unsafe fn remove_bundle_from_archetype(
// check the archetype graph to see if the Bundle has been removed from this archetype in the
// past
let remove_bundle_result = {
// SAFE: entity location is valid and therefore the archetype exists
let current_archetype = archetypes.get_unchecked_mut(archetype_id);
let current_archetype = &mut archetypes[archetype_id];
if intersection {
current_archetype
.edges()
@ -811,8 +758,7 @@ unsafe fn remove_bundle_from_archetype(
let mut next_sparse_set_components;
let next_table_id;
{
// SAFE: entity location is valid and therefore the archetype exists
let current_archetype = archetypes.get_unchecked_mut(archetype_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.component_ids.iter().cloned() {
@ -863,8 +809,7 @@ unsafe fn remove_bundle_from_archetype(
);
Some(new_archetype_id)
};
// SAFE: entity location is valid and therefore the archetype exists
let current_archetype = archetypes.get_unchecked_mut(archetype_id);
let current_archetype = &mut archetypes[archetype_id];
// cache the result in an edge
if intersection {
current_archetype

View file

@ -269,11 +269,10 @@ impl World {
let archetype = self.archetypes.empty_mut();
unsafe {
// PERF: consider avoiding allocating entities in the empty archetype unless needed
// SAFE: archetype tables always exist
let table = self.storages.tables.get_unchecked_mut(archetype.table_id());
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
// SAFE: no components are allocated by archetype.allocate() because the archetype is
// empty
let location = archetype.allocate(entity, table.allocate(entity));
let location = archetype.allocate(entity, table_row);
// SAFE: entity index was just allocated
self.entities
.meta
@ -801,11 +800,7 @@ impl World {
pub(crate) fn flush(&mut self) {
let empty_archetype = self.archetypes.empty_mut();
unsafe {
// SAFE: archetype tables always exist
let table = self
.storages
.tables
.get_unchecked_mut(empty_archetype.table_id());
let table = &mut self.storages.tables[empty_archetype.table_id()];
// PERF: consider pre-allocating space for flushed entities
self.entities.flush(|entity, location| {
// SAFE: no components are allocated by archetype.allocate() because the archetype

View file

@ -45,15 +45,8 @@ where
bundle_info,
)
};
// SAFE: archetype exists
let archetype = unsafe { world.archetypes.get_unchecked_mut(archetype_id) };
// SAFE: table exists
let table = unsafe {
world
.storages
.tables
.get_unchecked_mut(archetype.table_id())
};
let archetype = &mut world.archetypes[archetype_id];
let table = &mut world.storages.tables[archetype.table_id()];
archetype.reserve(length);
table.reserve(length);
world.entities.reserve(length as u32);