mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 20:53:53 +00:00
ecs: Added<T> queries
This commit is contained in:
parent
21cdaaf7eb
commit
a6953049fb
4 changed files with 133 additions and 22 deletions
|
@ -134,6 +134,13 @@ impl Archetype {
|
|||
Some(unsafe { NonNull::new_unchecked(state.modified_entities.as_ptr() as *mut bool) })
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[inline]
|
||||
pub fn get_added<T: Component>(&self) -> Option<NonNull<bool>> {
|
||||
let state = self.state.get(&TypeId::of::<T>())?;
|
||||
Some(unsafe { NonNull::new_unchecked(state.added_entities.as_ptr() as *mut bool) })
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
|
||||
self.state.get_mut(&ty)
|
||||
|
@ -258,6 +265,7 @@ impl Archetype {
|
|||
|
||||
for type_state in self.state.values_mut() {
|
||||
type_state.modified_entities.resize_with(count, || false);
|
||||
type_state.added_entities.resize_with(count, || false);
|
||||
}
|
||||
|
||||
let old_data_size = mem::replace(&mut self.data_size, 0);
|
||||
|
@ -318,6 +326,7 @@ impl Archetype {
|
|||
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
|
||||
type_state.added_entities[index as usize] = type_state.added_entities[last as usize];
|
||||
}
|
||||
}
|
||||
self.len = last;
|
||||
|
@ -333,7 +342,7 @@ impl Archetype {
|
|||
pub(crate) unsafe fn move_to(
|
||||
&mut self,
|
||||
index: u32,
|
||||
mut f: impl FnMut(*mut u8, TypeId, usize, bool),
|
||||
mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
|
||||
) -> Option<u32> {
|
||||
let last = self.len - 1;
|
||||
for ty in &self.types {
|
||||
|
@ -342,8 +351,9 @@ impl Archetype {
|
|||
.unwrap()
|
||||
.as_ptr();
|
||||
let type_state = self.state.get(&ty.id).unwrap();
|
||||
let is_added= type_state.added_entities[index as usize];
|
||||
let is_modified = type_state.modified_entities[index as usize];
|
||||
f(moved, ty.id(), ty.layout().size(), is_modified);
|
||||
f(moved, ty.id(), ty.layout().size(), is_added, is_modified);
|
||||
if index != last {
|
||||
ptr::copy_nonoverlapping(
|
||||
self.get_dynamic(ty.id, ty.layout.size(), last)
|
||||
|
@ -353,6 +363,7 @@ impl Archetype {
|
|||
ty.layout.size(),
|
||||
);
|
||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
||||
type_state.added_entities[index as usize] = type_state.added_entities[last as usize];
|
||||
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
|
||||
}
|
||||
}
|
||||
|
@ -366,12 +377,15 @@ impl Archetype {
|
|||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32) {
|
||||
let ptr = self
|
||||
.get_dynamic(ty, size, index)
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
.cast::<u8>();
|
||||
pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32, added: bool) {
|
||||
let state = self.state.get_mut(&ty).unwrap();
|
||||
if added {
|
||||
state.added_entities[index as usize] = true;
|
||||
}
|
||||
let ptr = (*self.data.get())
|
||||
.as_ptr()
|
||||
.add(state.offset + size * index as usize)
|
||||
.cast::<u8>();
|
||||
ptr::copy_nonoverlapping(component, ptr, size);
|
||||
}
|
||||
|
||||
|
@ -402,6 +416,7 @@ pub struct TypeState {
|
|||
offset: usize,
|
||||
borrow: AtomicBorrow,
|
||||
pub modified_entities: Vec<bool>,
|
||||
pub added_entities: Vec<bool>,
|
||||
}
|
||||
|
||||
impl TypeState {
|
||||
|
@ -410,6 +425,7 @@ impl TypeState {
|
|||
offset: 0,
|
||||
borrow: AtomicBorrow::new(),
|
||||
modified_entities: Vec::new(),
|
||||
added_entities: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -417,6 +433,10 @@ impl TypeState {
|
|||
for modified in self.modified_entities.iter_mut() {
|
||||
*modified = false;
|
||||
}
|
||||
|
||||
for added in self.added_entities.iter_mut() {
|
||||
*added = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,7 +150,6 @@ impl<T: Query> Query for Option<T> {
|
|||
type Fetch = TryFetch<T::Fetch>;
|
||||
}
|
||||
|
||||
|
||||
/// Unique borrow of an entity's component
|
||||
pub struct Mut<'a, T: Component> {
|
||||
value: &'a mut T,
|
||||
|
@ -279,6 +278,61 @@ impl<'a, T: Component> Fetch<'a> for FetchChanged<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub struct Added<'a, T> {
|
||||
value: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Deref for Added<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Component> Query for Added<'a, T> {
|
||||
type Fetch = FetchAdded<T>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FetchAdded<T>(NonNull<T>, NonNull<bool>);
|
||||
|
||||
impl<'a, T: Component> Fetch<'a> for FetchAdded<T> {
|
||||
type Item = Added<'a, T>;
|
||||
|
||||
fn access(archetype: &Archetype) -> Option<Access> {
|
||||
if archetype.has::<T>() {
|
||||
Some(Access::Read)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow(archetype: &Archetype) {
|
||||
archetype.borrow::<T>();
|
||||
}
|
||||
unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option<Self> {
|
||||
let components = NonNull::new_unchecked(archetype.get::<T>()?.as_ptr().add(offset));
|
||||
let modified = NonNull::new_unchecked(archetype.get_added::<T>()?.as_ptr().add(offset));
|
||||
Some(Self(components, modified))
|
||||
}
|
||||
fn release(archetype: &Archetype) {
|
||||
archetype.release::<T>();
|
||||
}
|
||||
|
||||
unsafe fn should_skip(&self) -> bool {
|
||||
// skip if the current item wasn't changed
|
||||
!*self.1.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn next(&mut self) -> Self::Item {
|
||||
self.1 = NonNull::new_unchecked(self.1.as_ptr().add(1));
|
||||
let value = self.0.as_ptr();
|
||||
self.0 = NonNull::new_unchecked(value.add(1));
|
||||
Added { value: &*value }
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub struct TryFetch<T>(Option<T>);
|
||||
|
||||
|
@ -782,6 +836,38 @@ mod tests {
|
|||
struct B(usize);
|
||||
struct C;
|
||||
|
||||
#[test]
|
||||
fn added_trackers() {
|
||||
let mut world = World::default();
|
||||
let e1 = world.spawn((A(0),));
|
||||
|
||||
fn get_added<Com: Component>(world: &World) -> Vec<Entity> {
|
||||
world
|
||||
.query::<(Added<Com>, Entity)>()
|
||||
.iter()
|
||||
.map(|(_added, e)| e)
|
||||
.collect::<Vec<Entity>>()
|
||||
};
|
||||
|
||||
assert_eq!(get_added::<A>(&world), vec![e1]);
|
||||
world.insert(e1, (B(0),)).unwrap();
|
||||
assert_eq!(get_added::<A>(&world), vec![e1]);
|
||||
assert_eq!(get_added::<B>(&world), vec![e1]);
|
||||
|
||||
world.clear_trackers();
|
||||
assert!(get_added::<A>(&world).is_empty());
|
||||
let e2 = world.spawn((A(1), B(1)));
|
||||
assert_eq!(get_added::<A>(&world), vec![e2]);
|
||||
assert_eq!(get_added::<B>(&world), vec![e2]);
|
||||
|
||||
let added = world
|
||||
.query::<(Entity, Added<A>, Added<B>)>()
|
||||
.iter()
|
||||
.map(|a| a.0)
|
||||
.collect::<Vec<Entity>>();
|
||||
assert_eq!(added, vec![e2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modified_trackers() {
|
||||
let mut world = World::default();
|
||||
|
|
|
@ -106,7 +106,7 @@ impl World {
|
|||
unsafe {
|
||||
let index = archetype.allocate(entity.id());
|
||||
components.put(|ptr, ty, size| {
|
||||
archetype.put_dynamic(ptr, ty, size, index);
|
||||
archetype.put_dynamic(ptr, ty, size, index, true);
|
||||
true
|
||||
});
|
||||
self.entities.insert(
|
||||
|
@ -371,7 +371,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);
|
||||
arch.put_dynamic(ptr, ty, size, loc.index, false);
|
||||
true
|
||||
});
|
||||
return Ok(());
|
||||
|
@ -386,14 +386,17 @@ impl World {
|
|||
let target_index = target_arch.allocate(entity.id());
|
||||
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_modified| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index);
|
||||
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
|
||||
if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_added, is_modified| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index, false);
|
||||
let type_state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
type_state.added_entities[target_index as usize] = is_added;
|
||||
type_state.modified_entities[target_index as usize] = is_modified;
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
}
|
||||
|
||||
components.put(|ptr, ty, size| {
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index);
|
||||
target_arch.put_dynamic(ptr, ty, size, target_index, true);
|
||||
true
|
||||
});
|
||||
}
|
||||
|
@ -464,11 +467,13 @@ impl World {
|
|||
let target_index = target_arch.allocate(entity.id());
|
||||
loc.archetype = target;
|
||||
loc.index = target_index;
|
||||
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_modified| {
|
||||
if let Some(moved) = source_arch.move_to(old_index, |src, ty, size, is_added, is_modified| {
|
||||
// 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);
|
||||
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
|
||||
let state = target_arch.get_type_state_mut(ty).unwrap();
|
||||
state.added_entities[target_index as usize] = is_added;
|
||||
state.modified_entities[target_index as usize] = is_modified;
|
||||
}
|
||||
}) {
|
||||
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||
|
@ -746,7 +751,7 @@ where
|
|||
unsafe {
|
||||
let index = self.archetype.allocate(entity.id());
|
||||
components.put(|ptr, ty, size| {
|
||||
self.archetype.put_dynamic(ptr, ty, size, index);
|
||||
self.archetype.put_dynamic(ptr, ty, size, index, true);
|
||||
true
|
||||
});
|
||||
self.entities.insert(
|
||||
|
|
|
@ -65,13 +65,13 @@ impl Resources {
|
|||
});
|
||||
|
||||
let archetype = &mut data.archetype;
|
||||
|
||||
let mut added = false;
|
||||
let index = match resource_index {
|
||||
ResourceIndex::Global => *data.default_index.get_or_insert_with(|| archetype.len()),
|
||||
ResourceIndex::Global => *data.default_index.get_or_insert_with(|| { added = true; archetype.len() }),
|
||||
ResourceIndex::System(id) => *data
|
||||
.system_id_to_archetype_index
|
||||
.entry(id.0)
|
||||
.or_insert_with(|| archetype.len()),
|
||||
.or_insert_with(|| { added = true; archetype.len() }),
|
||||
};
|
||||
|
||||
if index == archetype.len() {
|
||||
|
@ -82,7 +82,7 @@ impl Resources {
|
|||
|
||||
unsafe {
|
||||
let resource_ptr = (&mut resource as *mut T).cast::<u8>();
|
||||
archetype.put_dynamic(resource_ptr, type_id, core::mem::size_of::<T>(), index);
|
||||
archetype.put_dynamic(resource_ptr, type_id, core::mem::size_of::<T>(), index, added);
|
||||
std::mem::forget(resource);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue