ecs: replace "bool" component states with bitflags (#878)

This commit is contained in:
Carter Anderson 2020-11-17 17:04:44 -08:00 committed by GitHub
parent d6eb647451
commit 457a8bd17d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 79 deletions

View File

@ -24,6 +24,7 @@ rand = "0.7.3"
serde = "1.0"
thiserror = "1.0"
fixedbitset = "0.3.1"
bitflags = "1.2.1"
downcast-rs = "1.2.0"
parking_lot = "0.11.0"
lazy_static = { version = "1.4.0" }

View File

@ -16,6 +16,7 @@
use crate::{AtomicBorrow, Component, Entity};
use bevy_utils::AHasher;
use bitflags::bitflags;
use std::{
alloc::{alloc, dealloc, Layout},
any::{type_name, TypeId},
@ -281,8 +282,9 @@ impl Archetype {
);
for type_state in self.state.values_mut() {
type_state.mutated_entities.resize_with(count, || false);
type_state.added_entities.resize_with(count, || false);
type_state
.component_flags
.resize_with(count, ComponentFlags::empty);
}
let old_data_size = mem::replace(&mut self.data_size, 0);
@ -349,8 +351,7 @@ impl Archetype {
);
let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.mutated_entities[index] = type_state.mutated_entities[last];
type_state.added_entities[index] = type_state.added_entities[last];
type_state.component_flags[index] = type_state.component_flags[last];
}
}
self.len = last;
@ -366,7 +367,7 @@ impl Archetype {
pub(crate) unsafe fn move_to(
&mut self,
index: usize,
mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
mut f: impl FnMut(*mut u8, TypeId, usize, ComponentFlags),
) -> Option<Entity> {
let last = self.len - 1;
for ty in &self.types {
@ -375,9 +376,8 @@ impl Archetype {
.unwrap()
.as_ptr();
let type_state = self.state.get(&ty.id).unwrap();
let is_added = type_state.added_entities[index];
let is_mutated = type_state.mutated_entities[index];
f(moved, ty.id(), ty.layout().size(), is_added, is_mutated);
let flags = type_state.component_flags[index];
f(moved, ty.id(), ty.layout().size(), flags);
if index != last {
ptr::copy_nonoverlapping(
self.get_dynamic(ty.id, ty.layout.size(), last)
@ -387,8 +387,7 @@ impl Archetype {
ty.layout.size(),
);
let type_state = self.state.get_mut(&ty.id).unwrap();
type_state.added_entities[index] = type_state.added_entities[last];
type_state.mutated_entities[index] = type_state.mutated_entities[last];
type_state.component_flags[index] = type_state.component_flags[last];
}
}
self.len -= 1;
@ -413,16 +412,10 @@ impl Archetype {
ty: TypeId,
size: usize,
index: usize,
added: bool,
mutated: bool,
flags: ComponentFlags,
) {
let state = self.state.get_mut(&ty).unwrap();
if added {
state.added_entities[index] = true;
}
if mutated {
state.mutated_entities[index] = true;
}
state.component_flags[index] = flags;
let ptr = (*self.data.get())
.as_ptr()
.add(state.offset + size * index)
@ -453,8 +446,14 @@ impl Drop for Archetype {
pub struct TypeState {
offset: usize,
borrow: AtomicBorrow,
mutated_entities: Vec<bool>,
added_entities: Vec<bool>,
component_flags: Vec<ComponentFlags>,
}
bitflags! {
pub struct ComponentFlags: u8 {
const ADDED = 1;
const MUTATED = 2;
}
}
impl TypeState {
@ -462,31 +461,20 @@ impl TypeState {
Self {
offset: 0,
borrow: AtomicBorrow::new(),
mutated_entities: Vec::new(),
added_entities: Vec::new(),
component_flags: Vec::new(),
}
}
fn clear_trackers(&mut self) {
for mutated in self.mutated_entities.iter_mut() {
*mutated = false;
}
for added in self.added_entities.iter_mut() {
*added = false;
for flags in self.component_flags.iter_mut() {
*flags = ComponentFlags::empty();
}
}
#[allow(missing_docs)]
#[inline]
pub fn mutated(&self) -> NonNull<bool> {
unsafe { NonNull::new_unchecked(self.mutated_entities.as_ptr() as *mut bool) }
}
#[allow(missing_docs)]
#[inline]
pub fn added(&self) -> NonNull<bool> {
unsafe { NonNull::new_unchecked(self.added_entities.as_ptr() as *mut bool) }
pub fn component_flags(&self) -> NonNull<ComponentFlags> {
unsafe { NonNull::new_unchecked(self.component_flags.as_ptr() as *mut ComponentFlags) }
}
}

View File

@ -14,14 +14,13 @@
// modified by Bevy contributors
use crate::{Archetype, Component, ComponentFlags, MissingComponent};
use core::{
fmt::Debug,
ops::{Deref, DerefMut},
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{Archetype, Component, MissingComponent};
/// Atomically enforces Rust-style borrow checking at runtime
#[derive(Debug)]
pub struct AtomicBorrow(AtomicUsize);
@ -125,7 +124,7 @@ where
pub struct RefMut<'a, T: Component> {
archetype: &'a Archetype,
target: &'a mut T,
modified: &'a mut bool,
flags: &'a mut ComponentFlags,
}
impl<'a, T: Component> RefMut<'a, T> {
@ -142,7 +141,7 @@ impl<'a, T: Component> RefMut<'a, T> {
Ok(Self {
archetype,
target: &mut *target.as_ptr().add(index),
modified: &mut *type_state.mutated().as_ptr().add(index),
flags: &mut *type_state.component_flags().as_ptr().add(index),
})
}
}
@ -166,7 +165,7 @@ impl<'a, T: Component> Deref for RefMut<'a, T> {
impl<'a, T: Component> DerefMut for RefMut<'a, T> {
fn deref_mut(&mut self) -> &mut T {
*self.modified = true;
self.flags.insert(ComponentFlags::MUTATED);
self.target
}
}

View File

@ -1,4 +1,4 @@
use crate::{Archetype, Bundle, Component, QueryAccess};
use crate::{core::ComponentFlags, Archetype, Bundle, Component, QueryAccess};
use std::{any::TypeId, marker::PhantomData, ptr::NonNull};
pub trait QueryFilter: Sized {
@ -31,13 +31,13 @@ pub struct Or<T>(pub T);
/// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame.
/// Added components do not count as mutated.
pub struct Mutated<T>(NonNull<bool>, PhantomData<T>);
pub struct Mutated<T>(NonNull<ComponentFlags>, PhantomData<T>);
/// Query transformer that retrieves components of type `T` that have been added since the start of the frame.
pub struct Added<T>(NonNull<bool>, PhantomData<T>);
pub struct Added<T>(NonNull<ComponentFlags>, PhantomData<T>);
/// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame.
pub struct Changed<T>(NonNull<bool>, NonNull<bool>, PhantomData<T>);
pub struct Changed<T>(NonNull<ComponentFlags>, PhantomData<T>);
impl QueryFilter for () {
type EntityFilter = AnyEntityFilter;
@ -63,7 +63,7 @@ impl<T: Component> QueryFilter for Added<T> {
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
archetype
.get_type_state(TypeId::of::<T>())
.map(|state| Added(state.added(), Default::default()))
.map(|state| Added(state.component_flags(), Default::default()))
}
}
@ -72,7 +72,7 @@ impl<T: Component> EntityFilter for Added<T> {
#[inline]
unsafe fn matches_entity(&self, offset: usize) -> bool {
*self.0.as_ptr().add(offset)
(*self.0.as_ptr().add(offset)).contains(ComponentFlags::ADDED)
}
}
@ -87,7 +87,7 @@ impl<T: Component> QueryFilter for Mutated<T> {
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
archetype
.get_type_state(TypeId::of::<T>())
.map(|state| Mutated(state.mutated(), Default::default()))
.map(|state| Mutated(state.component_flags(), Default::default()))
}
}
@ -95,7 +95,7 @@ impl<T: Component> EntityFilter for Mutated<T> {
const DANGLING: Self = Mutated(NonNull::dangling(), PhantomData::<T>);
unsafe fn matches_entity(&self, offset: usize) -> bool {
*self.0.as_ptr().add(offset)
(*self.0.as_ptr().add(offset)).contains(ComponentFlags::MUTATED)
}
}
@ -110,16 +110,17 @@ impl<T: Component> QueryFilter for Changed<T> {
fn get_entity_filter(archetype: &Archetype) -> Option<Self::EntityFilter> {
archetype
.get_type_state(TypeId::of::<T>())
.map(|state| Changed(state.added(), state.mutated(), Default::default()))
.map(|state| Changed(state.component_flags(), Default::default()))
}
}
impl<T: Component> EntityFilter for Changed<T> {
const DANGLING: Self = Changed(NonNull::dangling(), NonNull::dangling(), PhantomData::<T>);
const DANGLING: Self = Changed(NonNull::dangling(), PhantomData::<T>);
#[inline]
unsafe fn matches_entity(&self, offset: usize) -> bool {
*self.0.as_ptr().add(offset) || *self.1.as_ptr().add(offset)
let flags = *self.0.as_ptr().add(offset);
flags.contains(ComponentFlags::ADDED) || flags.contains(ComponentFlags::MUTATED)
}
}

View File

@ -44,7 +44,7 @@ mod world;
mod world_builder;
pub use access::{ArchetypeComponent, QueryAccess, TypeAccess};
pub use archetype::{Archetype, TypeState};
pub use archetype::{Archetype, ComponentFlags, TypeState};
pub use borrow::{AtomicBorrow, Ref, RefMut};
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};

View File

@ -14,9 +14,8 @@
// modified by Bevy contributors
use crate::EntityFilter;
use super::{Archetype, Component, Entity, MissingComponent, QueryAccess, QueryFilter};
use crate::{ComponentFlags, EntityFilter};
use std::{
marker::PhantomData,
ops::{Deref, DerefMut},
@ -134,7 +133,7 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
/// Unique borrow of an entity's component
pub struct Mut<'a, T: Component> {
pub(crate) value: &'a mut T,
pub(crate) mutated: &'a mut bool,
pub(crate) flags: &'a mut ComponentFlags,
}
impl<'a, T: Component> Mut<'a, T> {
@ -148,7 +147,7 @@ impl<'a, T: Component> Mut<'a, T> {
.ok_or_else(MissingComponent::new::<T>)?;
Ok(Self {
value: &mut *target.as_ptr().add(index),
mutated: &mut *type_state.mutated().as_ptr().add(index),
flags: &mut *type_state.component_flags().as_ptr().add(index),
})
}
}
@ -168,7 +167,7 @@ impl<'a, T: Component> Deref for Mut<'a, T> {
impl<'a, T: Component> DerefMut for Mut<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
*self.mutated = true;
self.flags.insert(ComponentFlags::MUTATED);
self.value
}
}
@ -183,7 +182,7 @@ impl<'a, T: Component> WorldQuery for Mut<'a, T> {
type Fetch = FetchMut<T>;
}
#[doc(hidden)]
pub struct FetchMut<T>(NonNull<T>, NonNull<bool>);
pub struct FetchMut<T>(NonNull<T>, NonNull<ComponentFlags>);
impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
type Item = Mut<'a, T>;
@ -196,7 +195,7 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
.map(|(components, type_state)| {
Self(
NonNull::new_unchecked(components.as_ptr().add(offset)),
NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)),
NonNull::new_unchecked(type_state.component_flags().as_ptr().add(offset)),
)
})
}
@ -205,7 +204,7 @@ impl<'a, T: Component> Fetch<'a> for FetchMut<T> {
unsafe fn fetch(&self, n: usize) -> Mut<'a, T> {
Mut {
value: &mut *self.0.as_ptr().add(n),
mutated: &mut *self.1.as_ptr().add(n),
flags: &mut *self.1.as_ptr().add(n),
}
}

View File

@ -15,9 +15,9 @@
// modified by Bevy contributors
use crate::{
core::entities::Entities, Archetype, BatchedIter, Bundle, DynamicBundle, Entity, EntityFilter,
EntityReserver, Fetch, Location, MissingComponent, Mut, NoSuchEntity, QueryFilter, QueryIter,
ReadOnlyFetch, Ref, RefMut, WorldQuery,
core::entities::Entities, Archetype, BatchedIter, Bundle, ComponentFlags, DynamicBundle,
Entity, EntityFilter, EntityReserver, Fetch, Location, MissingComponent, Mut, NoSuchEntity,
QueryFilter, QueryIter, ReadOnlyFetch, Ref, RefMut, WorldQuery,
};
use bevy_utils::{HashMap, HashSet};
use std::{any::TypeId, fmt, mem, ptr};
@ -96,7 +96,7 @@ impl World {
unsafe {
let index = archetype.allocate(entity);
components.put(|ptr, ty, size| {
archetype.put_dynamic(ptr, ty, size, index, true, false);
archetype.put_dynamic(ptr, ty, size, index, ComponentFlags::ADDED);
true
});
self.entities.meta[entity.id as usize].location = Location {
@ -602,7 +602,7 @@ impl World {
// Update components in the current archetype
let arch = &mut self.archetypes[loc.archetype as usize];
components.put(|ptr, ty, size| {
arch.put_dynamic(ptr, ty, size, loc.index, false, true);
arch.put_dynamic(ptr, ty, size, loc.index, ComponentFlags::MUTATED);
true
});
return Ok(());
@ -617,20 +617,22 @@ impl World {
let target_index = target_arch.allocate(entity);
loc.archetype = target;
let old_index = mem::replace(&mut loc.index, target_index);
if let Some(moved) =
source_arch.move_to(old_index, |ptr, ty, size, is_added, is_mutated| {
target_arch.put_dynamic(ptr, ty, size, target_index, false, false);
let type_state = target_arch.get_type_state_mut(ty).unwrap();
*type_state.added().as_ptr().add(target_index) = is_added;
*type_state.mutated().as_ptr().add(target_index) = is_mutated;
})
{
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, flags| {
target_arch.put_dynamic(ptr, ty, size, target_index, ComponentFlags::empty());
let type_state = target_arch.get_type_state_mut(ty).unwrap();
*type_state.component_flags().as_ptr().add(target_index) = flags;
}) {
self.entities.get_mut(moved).unwrap().index = old_index;
}
components.put(|ptr, ty, size| {
let had_component = source_arch.has_dynamic(ty);
target_arch.put_dynamic(ptr, ty, size, target_index, !had_component, had_component);
let flags = if had_component {
ComponentFlags::MUTATED
} else {
ComponentFlags::ADDED
};
target_arch.put_dynamic(ptr, ty, size, target_index, flags);
true
});
}
@ -719,13 +721,12 @@ impl World {
loc.index = target_index;
let removed_components = &mut self.removed_components;
if let Some(moved) = unsafe {
source_arch.move_to(old_index, |src, ty, size, is_added, is_mutated| {
source_arch.move_to(old_index, |src, ty, size, flags| {
// Only move the components present in the target archetype, i.e. the non-removed ones.
if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) {
ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
let state = target_arch.get_type_state_mut(ty).unwrap();
*state.added().as_ptr().add(target_index) = is_added;
*state.mutated().as_ptr().add(target_index) = is_mutated;
*state.component_flags().as_ptr().add(target_index) = flags;
} else {
let removed_entities = removed_components.entry(ty).or_insert_with(Vec::new);
removed_entities.push(entity);
@ -1117,7 +1118,7 @@ where
let index = self.archetype.allocate(entity);
components.put(|ptr, ty, size| {
self.archetype
.put_dynamic(ptr, ty, size, index, true, false);
.put_dynamic(ptr, ty, size, index, ComponentFlags::ADDED);
true
});
self.entities.meta[entity.id as usize].location = Location {