ecs: replace "bool" component states with bitflags (#878)
This commit is contained in:
parent
d6eb647451
commit
457a8bd17d
@ -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" }
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user