mirror of
https://github.com/bevyengine/bevy
synced 2024-11-23 05:03:47 +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) })
|
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)]
|
#[allow(missing_docs)]
|
||||||
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
|
pub fn get_type_state_mut(&mut self, ty: TypeId) -> Option<&mut TypeState> {
|
||||||
self.state.get_mut(&ty)
|
self.state.get_mut(&ty)
|
||||||
|
@ -258,6 +265,7 @@ impl Archetype {
|
||||||
|
|
||||||
for type_state in self.state.values_mut() {
|
for type_state in self.state.values_mut() {
|
||||||
type_state.modified_entities.resize_with(count, || false);
|
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);
|
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();
|
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.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;
|
self.len = last;
|
||||||
|
@ -333,7 +342,7 @@ impl Archetype {
|
||||||
pub(crate) unsafe fn move_to(
|
pub(crate) unsafe fn move_to(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: u32,
|
index: u32,
|
||||||
mut f: impl FnMut(*mut u8, TypeId, usize, bool),
|
mut f: impl FnMut(*mut u8, TypeId, usize, bool, bool),
|
||||||
) -> Option<u32> {
|
) -> Option<u32> {
|
||||||
let last = self.len - 1;
|
let last = self.len - 1;
|
||||||
for ty in &self.types {
|
for ty in &self.types {
|
||||||
|
@ -342,8 +351,9 @@ impl Archetype {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ptr();
|
.as_ptr();
|
||||||
let type_state = self.state.get(&ty.id).unwrap();
|
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];
|
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 {
|
if index != last {
|
||||||
ptr::copy_nonoverlapping(
|
ptr::copy_nonoverlapping(
|
||||||
self.get_dynamic(ty.id, ty.layout.size(), last)
|
self.get_dynamic(ty.id, ty.layout.size(), last)
|
||||||
|
@ -353,6 +363,7 @@ impl Archetype {
|
||||||
ty.layout.size(),
|
ty.layout.size(),
|
||||||
);
|
);
|
||||||
let type_state = self.state.get_mut(&ty.id).unwrap();
|
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];
|
type_state.modified_entities[index as usize] = type_state.modified_entities[last as usize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,11 +377,14 @@ impl Archetype {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32) {
|
pub unsafe fn put_dynamic(&mut self, component: *mut u8, ty: TypeId, size: usize, index: u32, added: bool) {
|
||||||
let ptr = self
|
let state = self.state.get_mut(&ty).unwrap();
|
||||||
.get_dynamic(ty, size, index)
|
if added {
|
||||||
.unwrap()
|
state.added_entities[index as usize] = true;
|
||||||
|
}
|
||||||
|
let ptr = (*self.data.get())
|
||||||
.as_ptr()
|
.as_ptr()
|
||||||
|
.add(state.offset + size * index as usize)
|
||||||
.cast::<u8>();
|
.cast::<u8>();
|
||||||
ptr::copy_nonoverlapping(component, ptr, size);
|
ptr::copy_nonoverlapping(component, ptr, size);
|
||||||
}
|
}
|
||||||
|
@ -402,6 +416,7 @@ pub struct TypeState {
|
||||||
offset: usize,
|
offset: usize,
|
||||||
borrow: AtomicBorrow,
|
borrow: AtomicBorrow,
|
||||||
pub modified_entities: Vec<bool>,
|
pub modified_entities: Vec<bool>,
|
||||||
|
pub added_entities: Vec<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TypeState {
|
impl TypeState {
|
||||||
|
@ -410,6 +425,7 @@ impl TypeState {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
borrow: AtomicBorrow::new(),
|
borrow: AtomicBorrow::new(),
|
||||||
modified_entities: Vec::new(),
|
modified_entities: Vec::new(),
|
||||||
|
added_entities: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,6 +433,10 @@ impl TypeState {
|
||||||
for modified in self.modified_entities.iter_mut() {
|
for modified in self.modified_entities.iter_mut() {
|
||||||
*modified = false;
|
*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>;
|
type Fetch = TryFetch<T::Fetch>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Unique borrow of an entity's component
|
/// Unique borrow of an entity's component
|
||||||
pub struct Mut<'a, T: Component> {
|
pub struct Mut<'a, T: Component> {
|
||||||
value: &'a mut T,
|
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)]
|
#[doc(hidden)]
|
||||||
pub struct TryFetch<T>(Option<T>);
|
pub struct TryFetch<T>(Option<T>);
|
||||||
|
|
||||||
|
@ -782,6 +836,38 @@ mod tests {
|
||||||
struct B(usize);
|
struct B(usize);
|
||||||
struct C;
|
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]
|
#[test]
|
||||||
fn modified_trackers() {
|
fn modified_trackers() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
|
|
|
@ -106,7 +106,7 @@ impl World {
|
||||||
unsafe {
|
unsafe {
|
||||||
let index = archetype.allocate(entity.id());
|
let index = archetype.allocate(entity.id());
|
||||||
components.put(|ptr, ty, size| {
|
components.put(|ptr, ty, size| {
|
||||||
archetype.put_dynamic(ptr, ty, size, index);
|
archetype.put_dynamic(ptr, ty, size, index, true);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
self.entities.insert(
|
self.entities.insert(
|
||||||
|
@ -371,7 +371,7 @@ impl World {
|
||||||
// Update components in the current archetype
|
// Update components in the current archetype
|
||||||
let arch = &mut self.archetypes[loc.archetype as usize];
|
let arch = &mut self.archetypes[loc.archetype as usize];
|
||||||
components.put(|ptr, ty, size| {
|
components.put(|ptr, ty, size| {
|
||||||
arch.put_dynamic(ptr, ty, size, loc.index);
|
arch.put_dynamic(ptr, ty, size, loc.index, false);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -386,14 +386,17 @@ impl World {
|
||||||
let target_index = target_arch.allocate(entity.id());
|
let target_index = target_arch.allocate(entity.id());
|
||||||
loc.archetype = target;
|
loc.archetype = target;
|
||||||
let old_index = mem::replace(&mut loc.index, target_index);
|
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| {
|
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);
|
target_arch.put_dynamic(ptr, ty, size, target_index, false);
|
||||||
target_arch.get_type_state_mut(ty).unwrap().modified_entities[target_index as usize] = is_modified;
|
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;
|
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
components.put(|ptr, ty, size| {
|
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
|
true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -464,11 +467,13 @@ impl World {
|
||||||
let target_index = target_arch.allocate(entity.id());
|
let target_index = target_arch.allocate(entity.id());
|
||||||
loc.archetype = target;
|
loc.archetype = target;
|
||||||
loc.index = target_index;
|
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.
|
// 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) {
|
if let Some(dst) = target_arch.get_dynamic(ty, size, target_index) {
|
||||||
ptr::copy_nonoverlapping(src, dst.as_ptr(), size);
|
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;
|
self.entities.get_mut(Entity::with_id(moved)).unwrap().index = old_index;
|
||||||
|
@ -746,7 +751,7 @@ where
|
||||||
unsafe {
|
unsafe {
|
||||||
let index = self.archetype.allocate(entity.id());
|
let index = self.archetype.allocate(entity.id());
|
||||||
components.put(|ptr, ty, size| {
|
components.put(|ptr, ty, size| {
|
||||||
self.archetype.put_dynamic(ptr, ty, size, index);
|
self.archetype.put_dynamic(ptr, ty, size, index, true);
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
self.entities.insert(
|
self.entities.insert(
|
||||||
|
|
|
@ -65,13 +65,13 @@ impl Resources {
|
||||||
});
|
});
|
||||||
|
|
||||||
let archetype = &mut data.archetype;
|
let archetype = &mut data.archetype;
|
||||||
|
let mut added = false;
|
||||||
let index = match resource_index {
|
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
|
ResourceIndex::System(id) => *data
|
||||||
.system_id_to_archetype_index
|
.system_id_to_archetype_index
|
||||||
.entry(id.0)
|
.entry(id.0)
|
||||||
.or_insert_with(|| archetype.len()),
|
.or_insert_with(|| { added = true; archetype.len() }),
|
||||||
};
|
};
|
||||||
|
|
||||||
if index == archetype.len() {
|
if index == archetype.len() {
|
||||||
|
@ -82,7 +82,7 @@ impl Resources {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let resource_ptr = (&mut resource as *mut T).cast::<u8>();
|
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);
|
std::mem::forget(resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue