diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 776d144153..44faa1178c 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -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" } diff --git a/crates/bevy_ecs/src/core/archetype.rs b/crates/bevy_ecs/src/core/archetype.rs index ea06b31ce0..4ff348f601 100644 --- a/crates/bevy_ecs/src/core/archetype.rs +++ b/crates/bevy_ecs/src/core/archetype.rs @@ -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 { 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, - added_entities: Vec, + component_flags: Vec, +} + +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 { - unsafe { NonNull::new_unchecked(self.mutated_entities.as_ptr() as *mut bool) } - } - - #[allow(missing_docs)] - #[inline] - pub fn added(&self) -> NonNull { - unsafe { NonNull::new_unchecked(self.added_entities.as_ptr() as *mut bool) } + pub fn component_flags(&self) -> NonNull { + unsafe { NonNull::new_unchecked(self.component_flags.as_ptr() as *mut ComponentFlags) } } } diff --git a/crates/bevy_ecs/src/core/borrow.rs b/crates/bevy_ecs/src/core/borrow.rs index 5420b94704..d1e677509b 100644 --- a/crates/bevy_ecs/src/core/borrow.rs +++ b/crates/bevy_ecs/src/core/borrow.rs @@ -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 } } diff --git a/crates/bevy_ecs/src/core/filter.rs b/crates/bevy_ecs/src/core/filter.rs index 6a126f5eda..65e01f4d58 100644 --- a/crates/bevy_ecs/src/core/filter.rs +++ b/crates/bevy_ecs/src/core/filter.rs @@ -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(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(NonNull, PhantomData); +pub struct Mutated(NonNull, PhantomData); /// Query transformer that retrieves components of type `T` that have been added since the start of the frame. -pub struct Added(NonNull, PhantomData); +pub struct Added(NonNull, PhantomData); /// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame. -pub struct Changed(NonNull, NonNull, PhantomData); +pub struct Changed(NonNull, PhantomData); impl QueryFilter for () { type EntityFilter = AnyEntityFilter; @@ -63,7 +63,7 @@ impl QueryFilter for Added { fn get_entity_filter(archetype: &Archetype) -> Option { archetype .get_type_state(TypeId::of::()) - .map(|state| Added(state.added(), Default::default())) + .map(|state| Added(state.component_flags(), Default::default())) } } @@ -72,7 +72,7 @@ impl EntityFilter for Added { #[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 QueryFilter for Mutated { fn get_entity_filter(archetype: &Archetype) -> Option { archetype .get_type_state(TypeId::of::()) - .map(|state| Mutated(state.mutated(), Default::default())) + .map(|state| Mutated(state.component_flags(), Default::default())) } } @@ -95,7 +95,7 @@ impl EntityFilter for Mutated { const DANGLING: Self = Mutated(NonNull::dangling(), PhantomData::); 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 QueryFilter for Changed { fn get_entity_filter(archetype: &Archetype) -> Option { archetype .get_type_state(TypeId::of::()) - .map(|state| Changed(state.added(), state.mutated(), Default::default())) + .map(|state| Changed(state.component_flags(), Default::default())) } } impl EntityFilter for Changed { - const DANGLING: Self = Changed(NonNull::dangling(), NonNull::dangling(), PhantomData::); + const DANGLING: Self = Changed(NonNull::dangling(), PhantomData::); #[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) } } diff --git a/crates/bevy_ecs/src/core/mod.rs b/crates/bevy_ecs/src/core/mod.rs index 0288064304..426e7bc879 100644 --- a/crates/bevy_ecs/src/core/mod.rs +++ b/crates/bevy_ecs/src/core/mod.rs @@ -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}; diff --git a/crates/bevy_ecs/src/core/query.rs b/crates/bevy_ecs/src/core/query.rs index b71e394578..0086ef6919 100644 --- a/crates/bevy_ecs/src/core/query.rs +++ b/crates/bevy_ecs/src/core/query.rs @@ -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 WorldQuery for Option { /// 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::)?; 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; } #[doc(hidden)] -pub struct FetchMut(NonNull, NonNull); +pub struct FetchMut(NonNull, NonNull); impl<'a, T: Component> Fetch<'a> for FetchMut { type Item = Mut<'a, T>; @@ -196,7 +195,7 @@ impl<'a, T: Component> Fetch<'a> for FetchMut { .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 { 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), } } diff --git a/crates/bevy_ecs/src/core/world.rs b/crates/bevy_ecs/src/core/world.rs index 4e6131cbcf..2a80f9ff03 100644 --- a/crates/bevy_ecs/src/core/world.rs +++ b/crates/bevy_ecs/src/core/world.rs @@ -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 {