diff --git a/src/all.rs b/src/all.rs index 9f10bf7..17d157e 100644 --- a/src/all.rs +++ b/src/all.rs @@ -24,12 +24,78 @@ pub struct ReadTraits<'a, Trait: ?Sized + TraitQuery> { sparse_sets: &'a SparseSets, } +/// Read-access to all components implementing a trait for a given entity. +pub struct AddedReadTraits<'a, Trait: ?Sized + TraitQuery> { + // Read-only access to the global trait registry. + // Since no one outside of the crate can name the registry type, + // we can be confident that no write accesses will conflict with this. + registry: &'a TraitImplRegistry, + table: &'a Table, + table_row: usize, + /// This grants shared access to all sparse set components, + /// but in practice we will only read the components specified in `self.registry`. + /// The fetch impl registers read-access for all of these components, + /// so there will be no runtime conflicts. + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +/// Read-access to all components implementing a trait for a given entity. +pub struct ChangedReadTraits<'a, Trait: ?Sized + TraitQuery> { + // Read-only access to the global trait registry. + // Since no one outside of the crate can name the registry type, + // we can be confident that no write accesses will conflict with this. + registry: &'a TraitImplRegistry, + table: &'a Table, + table_row: usize, + /// This grants shared access to all sparse set components, + /// but in practice we will only read the components specified in `self.registry`. + /// The fetch impl registers read-access for all of these components, + /// so there will be no runtime conflicts. + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +#[doc(hidden)] +pub struct ReadTableTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + table_row: usize, + // Grants shared access to the components corresponding to `components` in this table. + // Not all components are guaranteed to exist in the table. + table: &'a Table, +} + +#[doc(hidden)] +pub struct AddedReadTableTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + table_row: usize, + // Grants shared access to the components corresponding to `components` in this table. + // Not all components are guaranteed to exist in the table. + table: &'a Table, + last_change_tick: u32, + change_tick: u32, +} + #[doc(hidden)] pub type CombinedReadTraitsIter<'a, Trait> = std::iter::Chain, ReadSparseTraitsIter<'a, Trait>>; #[doc(hidden)] -pub struct ReadTableTraitsIter<'a, Trait: ?Sized> { +pub type CombinedAddedReadTraitsIter<'a, Trait> = + std::iter::Chain, AddedReadSparseTraitsIter<'a, Trait>>; + +#[doc(hidden)] +pub type CombinedChangedReadTraitsIter<'a, Trait> = + std::iter::Chain, ChangedReadSparseTraitsIter<'a, Trait>>; + +#[doc(hidden)] +pub struct ChangedReadTableTraitsIter<'a, Trait: ?Sized> { // SAFETY: These two iterators must have equal length. components: std::slice::Iter<'a, ComponentId>, meta: std::slice::Iter<'a, TraitImplMeta>, @@ -37,6 +103,8 @@ pub struct ReadTableTraitsIter<'a, Trait: ?Sized> { // Grants shared access to the components corresponding to `components` in this table. // Not all components are guaranteed to exist in the table. table: &'a Table, + last_change_tick: u32, + change_tick: u32, } impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadTableTraitsIter<'a, Trait> { @@ -57,6 +125,58 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadTableTraitsIter<'a, Trait> } } +impl<'a, Trait: ?Sized + TraitQuery> Iterator for AddedReadTableTraitsIter<'a, Trait> { + type Item = &'a Trait; + fn next(&mut self) -> Option { + // Iterate the remaining table components that are registered, + // until we find one that exists in the table. + let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; + + // SAFETY: we know that the table row is a valid index??? + let column_ticks = unsafe { column.get_ticks_unchecked(self.table_row).deref() }; + column_ticks + .is_added(self.last_change_tick, self.change_tick) + .then(|| unsafe { + // SAFETY ISSUE! SAFETY ISSUE! SAFETY ISSUE! Unlike in the write case, we do not have + // exclusive access! We have shared access to the entire column and ticks? This might be + // okay though because there cannot be any other accesses with write access at the same + // time? + let ptr = column + .get_data_ptr() + .byte_add(self.table_row * meta.size_bytes); + meta.dyn_ctor.cast(ptr) + }) + .or_else(|| self.next()) + } +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for ChangedReadTableTraitsIter<'a, Trait> { + type Item = &'a Trait; + fn next(&mut self) -> Option { + // Iterate the remaining table components that are registered, + // until we find one that exists in the table. + let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; + + // SAFETY ISSUE! SAFETY ISSUE! SAFETY ISSUE!: we know that the table row is a valid index??? + let column_ticks = unsafe { column.get_ticks_unchecked(self.table_row).deref() }; + column_ticks + .is_changed(self.last_change_tick, self.change_tick) + .then(|| unsafe { + // SAFETY ISSUE! SAFETY ISSUE! SAFETY ISSUE! Unlike in the write case, we do not have + // exclusive access! We have shared access to the entire column and ticks? This might be + // okay though because there cannot be any other accesses with write access at the same + // time? + let ptr = column + .get_data_ptr() + .byte_add(self.table_row * meta.size_bytes); + meta.dyn_ctor.cast(ptr) + }) + .or_else(|| self.next()) + } +} + #[doc(hidden)] pub struct ReadSparseTraitsIter<'a, Trait: ?Sized> { // SAFETY: These two iterators must have equal length. @@ -67,6 +187,30 @@ pub struct ReadSparseTraitsIter<'a, Trait: ?Sized> { sparse_sets: &'a SparseSets, } +#[doc(hidden)] +pub struct AddedReadSparseTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + entity: Entity, + // Grants shared access to the components corresponding to both `components` and `entity`. + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +#[doc(hidden)] +pub struct ChangedReadSparseTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + entity: Entity, + // Grants shared access to the components corresponding to both `components` and `entity`. + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadSparseTraitsIter<'a, Trait> { type Item = &'a Trait; fn next(&mut self) -> Option { @@ -85,6 +229,60 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadSparseTraitsIter<'a, Trait } } +impl<'a, Trait: ?Sized + TraitQuery> Iterator for AddedReadSparseTraitsIter<'a, Trait> { + type Item = &'a Trait; + fn next(&mut self) -> Option { + // Iterate the remaining sparse set components that are registered, + // until we find one that exists in the archetype. + let ((ptr, ticks_ptr), meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| { + self.sparse_sets + .get(component) + .and_then(|set| set.get_with_ticks(self.entity)) + .zip(Some(meta)) + })?; + + // SAFETY ISSUE! SAFETY ISSUE! SAFETY ISSUE! Unlike in the write case, we do not have + // exclusive access! We have shared access to the entire column and ticks? This might be + // okay though because there cannot be any other accesses with write access at the same + // time? + unsafe { + ticks_ptr + .deref() + .is_added(self.last_change_tick, self.change_tick) + .then(|| meta.dyn_ctor.cast(ptr)) + .or_else(|| self.next()) + } + } +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for ChangedReadSparseTraitsIter<'a, Trait> { + type Item = &'a Trait; + fn next(&mut self) -> Option { + // Iterate the remaining sparse set components that are registered, + // until we find one that exists in the archetype. + let ((ptr, ticks_ptr), meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| { + self.sparse_sets + .get(component) + .and_then(|set| set.get_with_ticks(self.entity)) + .zip(Some(meta)) + })?; + + // SAFETY ISSUE! SAFETY ISSUE! SAFETY ISSUE! Unlike in the write case, we do not have + // exclusive access! We have shared access to the entire column and ticks? This might be + // okay though because there cannot be any other accesses with write access at the same + // time? + unsafe { + ticks_ptr + .deref() + .is_changed(self.last_change_tick, self.change_tick) + .then(|| meta.dyn_ctor.cast(ptr)) + .or_else(|| self.next()) + } + } +} + impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ReadTraits<'w, Trait> { type Item = &'w Trait; type IntoIter = CombinedReadTraitsIter<'w, Trait>; @@ -106,6 +304,56 @@ impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ReadTraits<'w, Trait> { } } +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for AddedReadTraits<'w, Trait> { + type Item = &'w Trait; + type IntoIter = CombinedAddedReadTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = AddedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = AddedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ChangedReadTraits<'w, Trait> { + type Item = &'w Trait; + type IntoIter = CombinedChangedReadTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ChangedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = ChangedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &ReadTraits<'w, Trait> { type Item = &'w Trait; type IntoIter = CombinedReadTraitsIter<'w, Trait>; @@ -127,6 +375,56 @@ impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &ReadTraits<'w, Trait> { } } +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &AddedReadTraits<'w, Trait> { + type Item = &'w Trait; + type IntoIter = CombinedAddedReadTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = AddedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = AddedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &ChangedReadTraits<'w, Trait> { + type Item = &'w Trait; + type IntoIter = CombinedChangedReadTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ChangedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = ChangedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + impl<'w, Trait: ?Sized + TraitQuery> ReadTraits<'w, Trait> { /// Returns an iterator over the components implementing `Trait` for the current entity. pub fn iter(&self) -> CombinedReadTraitsIter<'w, Trait> { @@ -134,11 +432,27 @@ impl<'w, Trait: ?Sized + TraitQuery> ReadTraits<'w, Trait> { } } +impl<'w, Trait: ?Sized + TraitQuery> AddedReadTraits<'w, Trait> { + /// Returns an iterator over the components implementing `Trait` for the current entity. + pub fn iter(&self) -> CombinedAddedReadTraitsIter<'w, Trait> { + self.into_iter() + } +} + +impl<'w, Trait: ?Sized + TraitQuery> ChangedReadTraits<'w, Trait> { + /// Returns an iterator over the components implementing `Trait` for the current entity. + pub fn iter(&self) -> CombinedChangedReadTraitsIter<'w, Trait> { + self.into_iter() + } +} + #[doc(hidden)] pub struct ReadAllTraitsFetch<'w, Trait: ?Sized> { registry: &'w TraitImplRegistry, table: Option<&'w Table>, sparse_sets: &'w SparseSets, + last_change_tick: u32, + change_tick: u32, } /// Write-access to all components implementing a trait for a given entity. @@ -161,40 +475,118 @@ pub struct WriteTraits<'a, Trait: ?Sized + TraitQuery> { sparse_sets: &'a SparseSets, } -#[doc(hidden)] -pub type CombinedWriteTraitsIter<'a, Trait> = - std::iter::Chain, WriteSparseTraitsIter<'a, Trait>>; +/// Write-access to all components implementing a trait for a given entity, and have been newly +/// added in the last tick. +pub struct AddedWriteTraits<'a, Trait: ?Sized + TraitQuery> { + // Read-only access to the global trait registry. + // Since no one outside of the crate can name the registry type, + // we can be confident that no write accesses will conflict with this. + registry: &'a TraitImplRegistry, -#[doc(hidden)] -pub struct WriteTableTraitsIter<'a, Trait: ?Sized> { - // SAFETY: These two iterators must have equal length. - components: std::slice::Iter<'a, ComponentId>, - meta: std::slice::Iter<'a, TraitImplMeta>, table: &'a Table, - /// SAFETY: Given the same trait type and same archetype, - /// no two instances of this struct may have the same `table_row`. table_row: usize, + last_change_tick: u32, change_tick: u32, + + /// This grants shared mutable access to all sparse set components, + /// but in practice we will only modify the components specified in `self.registry`. + /// The fetch impl registers write-access for all of these components, + /// guaranteeing us exclusive access at runtime. + sparse_sets: &'a SparseSets, } -impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteTableTraitsIter<'a, Trait> { - type Item = Mut<'a, Trait>; - fn next(&mut self) -> Option { - // Iterate the remaining table components that are registered, - // until we find one that exists in the table. - let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } - .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; - let ptr = unsafe { - column - .get_data_ptr() - .byte_add(self.table_row * meta.size_bytes) - }; - // SAFETY: The instance of `WriteTraits` that created this iterator - // has exclusive access to all table components registered with the trait. - // - // Since `self.table_row` is guaranteed to be unique, we know that other instances - // of `WriteTableTraitsIter` will not conflict with this pointer. +/// Write-access to all components implementing a trait for a given entity, and have been changed in +/// the last tick. +pub struct ChangedWriteTraits<'a, Trait: ?Sized + TraitQuery> { + // Read-only access to the global trait registry. + // Since no one outside of the crate can name the registry type, + // we can be confident that no write accesses will conflict with this. + registry: &'a TraitImplRegistry, + + table: &'a Table, + table_row: usize, + + last_change_tick: u32, + change_tick: u32, + + /// This grants shared mutable access to all sparse set components, + /// but in practice we will only modify the components specified in `self.registry`. + /// The fetch impl registers write-access for all of these components, + /// guaranteeing us exclusive access at runtime. + sparse_sets: &'a SparseSets, +} + +#[doc(hidden)] +pub type CombinedWriteTraitsIter<'a, Trait> = + std::iter::Chain, WriteSparseTraitsIter<'a, Trait>>; + +#[doc(hidden)] +pub type CombinedAddedWriteTraitsIter<'a, Trait> = + std::iter::Chain, AddedWriteSparseTraitsIter<'a, Trait>>; + +#[doc(hidden)] +pub type CombinedChangedWriteTraitsIter<'a, Trait> = std::iter::Chain< + ChangedWriteTableTraitsIter<'a, Trait>, + ChangedWriteSparseTraitsIter<'a, Trait>, +>; + +#[doc(hidden)] +pub struct WriteTableTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + table: &'a Table, + /// SAFETY: Given the same trait type and same archetype, + /// no two instances of this struct may have the same `table_row`. + table_row: usize, + last_change_tick: u32, + change_tick: u32, +} + +#[doc(hidden)] +pub struct AddedWriteTableTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + table: &'a Table, + /// SAFETY: Given the same trait type and same archetype, + /// no two instances of this struct may have the same `table_row`. + table_row: usize, + last_change_tick: u32, + change_tick: u32, +} + +#[doc(hidden)] +pub struct ChangedWriteTableTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + table: &'a Table, + /// SAFETY: Given the same trait type and same archetype, + /// no two instances of this struct may have the same `table_row`. + table_row: usize, + last_change_tick: u32, + change_tick: u32, +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteTableTraitsIter<'a, Trait> { + type Item = Mut<'a, Trait>; + fn next(&mut self) -> Option { + // Iterate the remaining table components that are registered, + // until we find one that exists in the table. + let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; + let ptr = unsafe { + column + .get_data_ptr() + .byte_add(self.table_row * meta.size_bytes) + }; + // SAFETY: The instance of `WriteTraits` that created this iterator + // has exclusive access to all table components registered with the trait. + // + // Since `self.table_row` is guaranteed to be unique, we know that other instances + // of `WriteTableTraitsIter` will not conflict with this pointer. let ptr = unsafe { ptr.assert_unique() }; let trait_object = unsafe { meta.dyn_ctor.cast_mut(ptr) }; // SAFETY: We have exclusive access to the component, so by extension @@ -211,6 +603,80 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteTableTraitsIter<'a, Trait } } +impl<'a, Trait: ?Sized + TraitQuery> Iterator for AddedWriteTableTraitsIter<'a, Trait> { + type Item = Mut<'a, Trait>; + fn next(&mut self) -> Option { + // Iterate the remaining table components that are registered, + // until we find one that exists in the table. + let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; + let ptr = unsafe { + column + .get_data_ptr() + .byte_add(self.table_row * meta.size_bytes) + }; + // SAFETY: The instance of `WriteTraits` that created this iterator + // has exclusive access to all table components registered with the trait. + // + // Since `self.table_row` is guaranteed to be unique, we know that other instances + // of `WriteTableTraitsIter` will not conflict with this pointer. + // + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + let ptr = unsafe { ptr.assert_unique() }; + let trait_object = unsafe { meta.dyn_ctor.cast_mut(ptr) }; + let component_ticks = unsafe { column.get_ticks_unchecked(self.table_row).deref_mut() }; + component_ticks + .is_added(self.last_change_tick, self.change_tick) + .then_some(Mut { + value: trait_object, + ticks: Ticks { + component_ticks, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }) + .or_else(|| self.next()) + } +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for ChangedWriteTableTraitsIter<'a, Trait> { + type Item = Mut<'a, Trait>; + fn next(&mut self) -> Option { + // Iterate the remaining table components that are registered, + // until we find one that exists in the table. + let (column, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) } + .find_map(|(&component, meta)| self.table.get_column(component).zip(Some(meta)))?; + let ptr = unsafe { + column + .get_data_ptr() + .byte_add(self.table_row * meta.size_bytes) + }; + // SAFETY: The instance of `WriteTraits` that created this iterator + // has exclusive access to all table components registered with the trait. + // + // Since `self.table_row` is guaranteed to be unique, we know that other instances + // of `WriteTableTraitsIter` will not conflict with this pointer. + // + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + let ptr = unsafe { ptr.assert_unique() }; + let trait_object = unsafe { meta.dyn_ctor.cast_mut(ptr) }; + let component_ticks = unsafe { column.get_ticks_unchecked(self.table_row).deref_mut() }; + component_ticks + .is_changed(self.last_change_tick, self.change_tick) + .then_some(Mut { + value: trait_object, + ticks: Ticks { + component_ticks, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }) + .or_else(|| self.next()) + } +} + #[doc(hidden)] pub struct WriteSparseTraitsIter<'a, Trait: ?Sized> { // SAFETY: These two iterators must have equal length. @@ -224,6 +690,32 @@ pub struct WriteSparseTraitsIter<'a, Trait: ?Sized> { change_tick: u32, } +#[doc(hidden)] +pub struct AddedWriteSparseTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + /// SAFETY: Given the same trait type and same archetype, + /// no two instances of this struct may have the same `entity`. + entity: Entity, + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +#[doc(hidden)] +pub struct ChangedWriteSparseTraitsIter<'a, Trait: ?Sized> { + // SAFETY: These two iterators must have equal length. + components: std::slice::Iter<'a, ComponentId>, + meta: std::slice::Iter<'a, TraitImplMeta>, + /// SAFETY: Given the same trait type and same archetype, + /// no two instances of this struct may have the same `entity`. + entity: Entity, + sparse_sets: &'a SparseSets, + last_change_tick: u32, + change_tick: u32, +} + impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteSparseTraitsIter<'a, Trait> { type Item = Mut<'a, Trait>; fn next(&mut self) -> Option { @@ -250,134 +742,879 @@ impl<'a, Trait: ?Sized + TraitQuery> Iterator for WriteSparseTraitsIter<'a, Trai // we have exclusive access to the corresponding `ComponentTicks`. let component_ticks = unsafe { component_ticks.deref_mut() }; - Some(Mut { - value: trait_object, - ticks: Ticks { - component_ticks, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - }, - }) + Some(Mut { + value: trait_object, + ticks: Ticks { + component_ticks, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }) + } +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for AddedWriteSparseTraitsIter<'a, Trait> { + type Item = Mut<'a, Trait>; + fn next(&mut self) -> Option { + // Iterate the remaining sparse set components we have registered, + // until we find one that exists in the archetype. + let ((ptr, component_ticks), meta) = + unsafe { zip_exact(&mut self.components, &mut self.meta) }.find_map( + |(&component, meta)| { + self.sparse_sets + .get(component) + .and_then(|set| set.get_with_ticks(self.entity)) + .zip(Some(meta)) + }, + )?; + + // SAFETY: The instance of `WriteTraits` that created this iterator + // has exclusive access to all sparse set components registered with the trait. + // + // Since `self.entity` is guaranteed to be unique, we know that other instances + // of `WriteSparseTraitsIter` will not conflict with this pointer. + let ptr = unsafe { ptr.assert_unique() }; + let trait_object = unsafe { meta.dyn_ctor.cast_mut(ptr) }; + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + let component_ticks = unsafe { component_ticks.deref_mut() }; + component_ticks + .is_added(self.last_change_tick, self.change_tick) + .then_some(Mut { + value: trait_object, + ticks: Ticks { + component_ticks, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }) + .or_else(|| self.next()) + } +} + +impl<'a, Trait: ?Sized + TraitQuery> Iterator for ChangedWriteSparseTraitsIter<'a, Trait> { + type Item = Mut<'a, Trait>; + fn next(&mut self) -> Option { + // Iterate the remaining sparse set components we have registered, + // until we find one that exists in the archetype. + let ((ptr, component_ticks), meta) = + unsafe { zip_exact(&mut self.components, &mut self.meta) }.find_map( + |(&component, meta)| { + self.sparse_sets + .get(component) + .and_then(|set| set.get_with_ticks(self.entity)) + .zip(Some(meta)) + }, + )?; + + // SAFETY: The instance of `WriteTraits` that created this iterator + // has exclusive access to all sparse set components registered with the trait. + // + // Since `self.entity` is guaranteed to be unique, we know that other instances + // of `WriteSparseTraitsIter` will not conflict with this pointer. + let ptr = unsafe { ptr.assert_unique() }; + let trait_object = unsafe { meta.dyn_ctor.cast_mut(ptr) }; + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + let component_ticks = unsafe { component_ticks.deref_mut() }; + component_ticks + .is_changed(self.last_change_tick, self.change_tick) + .then_some(Mut { + value: trait_object, + ticks: Ticks { + component_ticks, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }, + }) + .or_else(|| self.next()) + } +} + +impl<'w, Trait: ?Sized + TraitQuery> WriteTraits<'w, Trait> { + /// Returns an iterator over the components implementing `Trait` for the current entity. + pub fn iter(&self) -> CombinedReadTraitsIter<'_, Trait> { + self.into_iter() + } + /// Returns a mutable iterator over the components implementing `Trait` for the current entity. + pub fn iter_mut(&mut self) -> CombinedWriteTraitsIter<'_, Trait> { + self.into_iter() + } +} + +impl<'w, Trait: ?Sized + TraitQuery> AddedWriteTraits<'w, Trait> { + /// Returns an iterator over the components implementing `Trait` for the current entity. + pub fn iter(&self) -> CombinedAddedReadTraitsIter<'_, Trait> { + self.into_iter() + } + /// Returns a mutable iterator over the components implementing `Trait` for the current entity. + pub fn iter_mut(&mut self) -> CombinedAddedWriteTraitsIter<'_, Trait> { + self.into_iter() + } +} + +impl<'w, Trait: ?Sized + TraitQuery> ChangedWriteTraits<'w, Trait> { + /// Returns an iterator over the components implementing `Trait` for the current entity. + pub fn iter(&self) -> CombinedChangedReadTraitsIter<'_, Trait> { + self.into_iter() + } + /// Returns a mutable iterator over the components implementing `Trait` for the current entity. + pub fn iter_mut(&mut self) -> CombinedChangedWriteTraitsIter<'_, Trait> { + self.into_iter() + } +} + +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for WriteTraits<'w, Trait> { + type Item = Mut<'w, Trait>; + type IntoIter = CombinedWriteTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = WriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = WriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for AddedWriteTraits<'w, Trait> { + type Item = Mut<'w, Trait>; + type IntoIter = CombinedAddedWriteTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = AddedWriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = AddedWriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ChangedWriteTraits<'w, Trait> { + type Item = Mut<'w, Trait>; + type IntoIter = CombinedChangedWriteTraitsIter<'w, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ChangedWriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = ChangedWriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local WriteTraits<'world, Trait> +{ + type Item = &'local Trait; + type IntoIter = CombinedReadTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + }; + let sparse = ReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local AddedWriteTraits<'world, Trait> +{ + type Item = &'local Trait; + type IntoIter = CombinedAddedReadTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = AddedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = AddedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local ChangedWriteTraits<'world, Trait> +{ + type Item = &'local Trait; + type IntoIter = CombinedChangedReadTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ChangedReadTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = ChangedReadSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local mut WriteTraits<'world, Trait> +{ + type Item = Mut<'local, Trait>; + type IntoIter = CombinedWriteTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = WriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = WriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local mut AddedWriteTraits<'world, Trait> +{ + type Item = Mut<'local, Trait>; + type IntoIter = CombinedAddedWriteTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = AddedWriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = AddedWriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator + for &'local mut ChangedWriteTraits<'world, Trait> +{ + type Item = Mut<'local, Trait>; + type IntoIter = CombinedChangedWriteTraitsIter<'local, Trait>; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let table = ChangedWriteTableTraitsIter { + components: self.registry.table_components.iter(), + meta: self.registry.table_meta.iter(), + table: self.table, + table_row: self.table_row, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + let sparse = ChangedWriteSparseTraitsIter { + components: self.registry.sparse_components.iter(), + meta: self.registry.sparse_meta.iter(), + entity: self.table.entities()[self.table_row], + sparse_sets: self.sparse_sets, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + }; + table.chain(sparse) + } +} + +#[doc(hidden)] +pub struct WriteAllTraitsFetch<'w, Trait: ?Sized + TraitQuery> { + registry: &'w TraitImplRegistry, + table: Option<&'w Table>, + sparse_sets: &'w SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity. +/// +/// You can usually just use `&dyn Trait` or `&mut dyn Trait` as a `WorldQuery` directly. +pub struct All(T); + +/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity, with +/// the additional condition that they have been added since the last tick. +pub struct AddedAll(T); + +/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity, with +/// the additional condition that they have also changed since the last tick. +pub struct ChangedAll(T); + +unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for All<&'a Trait> {} + +unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for AddedAll<&'a Trait> {} + +unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for ChangedAll<&'a Trait> {} + +/// SAFETY: We only access the components registered in the trait registry. +/// This is known to match the set of components in the `DynQueryState`, +/// which is used to match archetypes and register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { + type Item<'w> = ReadTraits<'w, Trait>; + type Fetch<'w> = ReadAllTraitsFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + _last_change_tick: u32, + _change_tick: u32, + ) -> ReadAllTraitsFetch<'w, Trait> { + ReadAllTraitsFetch { + registry: world.resource(), + table: None, + sparse_sets: &world.storages().sparse_sets, + last_change_tick: 0, + change_tick: 0, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + ReadAllTraitsFetch { + registry: fetch.registry, + table: fetch.table, + sparse_sets: fetch.sparse_sets, + last_change_tick: 0, + change_tick: 0, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + unsafe fn set_table<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + _entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let table = fetch.table.unwrap_or_else(|| debug_unreachable()); + + ReadTraits { + registry: fetch.registry, + table, + table_row, + sparse_sets: fetch.sparse_sets, + } + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_any(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in the trait registry. +/// This is known to match the set of components in the `DynQueryState`, +/// which is used to match archetypes and register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for ChangedAll<&'a Trait> { + type Item<'w> = ChangedReadTraits<'w, Trait>; + type Fetch<'w> = ReadAllTraitsFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> ReadAllTraitsFetch<'w, Trait> { + ReadAllTraitsFetch { + registry: world.resource(), + table: None, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + ReadAllTraitsFetch { + registry: fetch.registry, + table: fetch.table, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + unsafe fn set_table<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + _entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let table = fetch.table.unwrap_or_else(|| debug_unreachable()); + + ChangedReadTraits { + registry: fetch.registry, + table, + table_row, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_any(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in the trait registry. +/// This is known to match the set of components in the `DynQueryState`, +/// which is used to match archetypes and register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for AddedAll<&'a Trait> { + type Item<'w> = AddedReadTraits<'w, Trait>; + type Fetch<'w> = ReadAllTraitsFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> ReadAllTraitsFetch<'w, Trait> { + ReadAllTraitsFetch { + registry: world.resource(), + table: None, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + ReadAllTraitsFetch { + registry: fetch.registry, + table: fetch.table, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + unsafe fn set_table<'w>( + fetch: &mut ReadAllTraitsFetch<'w, Trait>, + _state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + _entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let table = fetch.table.unwrap_or_else(|| debug_unreachable()); + + AddedReadTraits { + registry: fetch.registry, + table, + table_row, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_any(set_contains_id) } } -impl<'w, Trait: ?Sized + TraitQuery> WriteTraits<'w, Trait> { - /// Returns an iterator over the components implementing `Trait` for the current entity. - pub fn iter(&self) -> CombinedReadTraitsIter<'_, Trait> { - self.into_iter() +/// SAFETY: We only access the components registered in the trait registry. +/// This is known to match the set of components in the `DynQueryState`, +/// which is used to match archetypes and register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> { + type Item<'w> = WriteTraits<'w, Trait>; + type Fetch<'w> = WriteAllTraitsFetch<'w, Trait>; + type ReadOnly = All<&'a Trait>; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item } - /// Returns a mutable iterator over the components implementing `Trait` for the current entity. - pub fn iter_mut(&mut self) -> CombinedWriteTraitsIter<'_, Trait> { - self.into_iter() + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> WriteAllTraitsFetch<'w, Trait> { + WriteAllTraitsFetch { + registry: world.resource(), + table: None, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } } -} -impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for WriteTraits<'w, Trait> { - type Item = Mut<'w, Trait>; - type IntoIter = CombinedWriteTraitsIter<'w, Trait>; #[inline] - fn into_iter(self) -> Self::IntoIter { - let table = WriteTableTraitsIter { - components: self.registry.table_components.iter(), - meta: self.registry.table_meta.iter(), - table: self.table, - table_row: self.table_row, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - }; - let sparse = WriteSparseTraitsIter { - components: self.registry.sparse_components.iter(), - meta: self.registry.sparse_meta.iter(), - entity: self.table.entities()[self.table_row], - sparse_sets: self.sparse_sets, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - }; - table.chain(sparse) + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + WriteAllTraitsFetch { + registry: fetch.registry, + table: fetch.table, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } } -} -impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator - for &'local WriteTraits<'world, Trait> -{ - type Item = &'local Trait; - type IntoIter = CombinedReadTraitsIter<'local, Trait>; + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + #[inline] - fn into_iter(self) -> Self::IntoIter { - let table = ReadTableTraitsIter { - components: self.registry.table_components.iter(), - meta: self.registry.table_meta.iter(), - table: self.table, - table_row: self.table_row, - }; - let sparse = ReadSparseTraitsIter { - components: self.registry.sparse_components.iter(), - meta: self.registry.sparse_meta.iter(), - entity: self.table.entities()[self.table_row], - sparse_sets: self.sparse_sets, - }; - table.chain(sparse) + unsafe fn set_archetype<'w>( + fetch: &mut WriteAllTraitsFetch<'w, Trait>, + _state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); } -} -impl<'world, 'local, Trait: ?Sized + TraitQuery> IntoIterator - for &'local mut WriteTraits<'world, Trait> -{ - type Item = Mut<'local, Trait>; - type IntoIter = CombinedWriteTraitsIter<'local, Trait>; #[inline] - fn into_iter(self) -> Self::IntoIter { - let table = WriteTableTraitsIter { - components: self.registry.table_components.iter(), - meta: self.registry.table_meta.iter(), - table: self.table, - table_row: self.table_row, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - }; - let sparse = WriteSparseTraitsIter { - components: self.registry.sparse_components.iter(), - meta: self.registry.sparse_meta.iter(), - entity: self.table.entities()[self.table_row], - sparse_sets: self.sparse_sets, - last_change_tick: self.last_change_tick, - change_tick: self.change_tick, - }; - table.chain(sparse) + unsafe fn set_table<'w>( + fetch: &mut WriteAllTraitsFetch<'w, Trait>, + _state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + fetch.table = Some(table); } -} -#[doc(hidden)] -pub struct WriteAllTraitsFetch<'w, Trait: ?Sized + TraitQuery> { - registry: &'w TraitImplRegistry, - table: Option<&'w Table>, - sparse_sets: &'w SparseSets, - last_change_tick: u32, - change_tick: u32, -} + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + _entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let table = fetch.table.unwrap_or_else(|| debug_unreachable()); -/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity. -/// -/// You can usually just use `&dyn Trait` or `&mut dyn Trait` as a `WorldQuery` directly. -pub struct All(T); + WriteTraits { + registry: fetch.registry, + table, + table_row, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } -/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity, with -/// the additional condition that they have also changed since the last tick. -pub struct ChangedAll(T); + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", + std::any::type_name::(), + ); + access.add_write(component); + } + } -/// `WorldQuery` adapter that fetches all implementations of a given trait for an entity, with -/// the additional condition that they have been added since the last tick. -pub struct AddedAll(T); + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_write(archetype_component_id); + } + } + } -unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for All<&'a Trait> {} + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_any(set_contains_id) + } +} /// SAFETY: We only access the components registered in the trait registry. /// This is known to match the set of components in the `DynQueryState`, /// which is used to match archetypes and register world access. -unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { - type Item<'w> = ReadTraits<'w, Trait>; - type Fetch<'w> = ReadAllTraitsFetch<'w, Trait>; - type ReadOnly = Self; +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for AddedAll<&'a mut Trait> { + type Item<'w> = AddedWriteTraits<'w, Trait>; + type Fetch<'w> = WriteAllTraitsFetch<'w, Trait>; + type ReadOnly = All<&'a Trait>; type State = TraitQueryState; #[inline] @@ -389,22 +1626,26 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { unsafe fn init_fetch<'w>( world: &'w World, _state: &Self::State, - _last_change_tick: u32, - _change_tick: u32, - ) -> ReadAllTraitsFetch<'w, Trait> { - ReadAllTraitsFetch { + last_change_tick: u32, + change_tick: u32, + ) -> WriteAllTraitsFetch<'w, Trait> { + WriteAllTraitsFetch { registry: world.resource(), table: None, sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, } } #[inline] unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { - ReadAllTraitsFetch { + WriteAllTraitsFetch { registry: fetch.registry, table: fetch.table, sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, } } @@ -413,7 +1654,7 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { #[inline] unsafe fn set_archetype<'w>( - fetch: &mut ReadAllTraitsFetch<'w, Trait>, + fetch: &mut WriteAllTraitsFetch<'w, Trait>, _state: &Self::State, _archetype: &'w bevy::ecs::archetype::Archetype, table: &'w bevy::ecs::storage::Table, @@ -421,8 +1662,9 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { fetch.table = Some(table); } + #[inline] unsafe fn set_table<'w>( - fetch: &mut ReadAllTraitsFetch<'w, Trait>, + fetch: &mut WriteAllTraitsFetch<'w, Trait>, _state: &Self::State, table: &'w bevy::ecs::storage::Table, ) { @@ -437,11 +1679,13 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { ) -> Self::Item<'w> { let table = fetch.table.unwrap_or_else(|| debug_unreachable()); - ReadTraits { + AddedWriteTraits { registry: fetch.registry, table, table_row, sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, } } @@ -453,10 +1697,10 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { for &component in &*state.components { assert!( !access.access().has_write(component), - "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", std::any::type_name::(), ); - access.add_read(component); + access.add_write(component); } } @@ -468,7 +1712,7 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { ) { for &component in &*state.components { if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { - access.add_read(archetype_component_id); + access.add_write(archetype_component_id); } } } @@ -489,8 +1733,8 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a Trait> { /// SAFETY: We only access the components registered in the trait registry. /// This is known to match the set of components in the `DynQueryState`, /// which is used to match archetypes and register world access. -unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> { - type Item<'w> = WriteTraits<'w, Trait>; +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for ChangedAll<&'a mut Trait> { + type Item<'w> = ChangedWriteTraits<'w, Trait>; type Fetch<'w> = WriteAllTraitsFetch<'w, Trait>; type ReadOnly = All<&'a Trait>; type State = TraitQueryState; @@ -557,7 +1801,7 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for All<&'a mut Trait> { ) -> Self::Item<'w> { let table = fetch.table.unwrap_or_else(|| debug_unreachable()); - WriteTraits { + ChangedWriteTraits { registry: fetch.registry, table, table_row, diff --git a/src/change_detection.rs b/src/change_detection.rs index 8cbacbe..69dcf61 100644 --- a/src/change_detection.rs +++ b/src/change_detection.rs @@ -1,5 +1,14 @@ -use bevy::ecs::component::ComponentTicks; +use crate::{debug_unreachable, TraitImplRegistry, TraitQuery, TraitQueryState}; +use bevy::ecs::archetype::{Archetype, ArchetypeComponentId}; +use bevy::ecs::component::{ComponentId, ComponentTicks}; +use bevy::ecs::entity::Entity; +use bevy::ecs::query::{Access, FilteredAccess, ReadOnlyWorldQuery, WorldQuery}; +use bevy::ecs::storage::{ComponentSparseSet, SparseSets, Table}; +use bevy::ecs::world::World; use bevy::prelude::DetectChanges; +use bevy::ptr::{ThinSlicePtr, UnsafeCellDeref}; +use std::cell::UnsafeCell; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; /// Unique mutable borrow of an entity's component @@ -103,3 +112,328 @@ impl AsMut for Mut<'_, T> { self.deref_mut() } } + +enum ChangeDetectionStorage<'w> { + Uninit, + Table { + /// This points to one of the component table columns, + /// corresponding to one of the `ComponentId`s in the fetch state. + /// The fetch impl registers read access for all of these components, + /// so there will be no runtime conflicts. + ticks: ThinSlicePtr<'w, UnsafeCell>, + }, + SparseSet { + /// This gives us access to one of the components implementing the trait. + /// The fetch impl registers read access for all components implementing the trait, + /// so there will not be any runtime conflicts. + components: &'w ComponentSparseSet, + }, +} + +pub struct OneAddedFilter<'a, Trait: ?Sized + TraitQuery> { + marker: PhantomData<&'a Trait>, +} + +pub struct ChangeDetectionFetch<'w, Trait: ?Sized + TraitQuery> { + registry: &'w TraitImplRegistry, + storage: ChangeDetectionStorage<'w>, + sparse_sets: &'w SparseSets, + last_change_tick: u32, + change_tick: u32, +} + +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for OneAddedFilter<'a, Trait> { + type Item<'w> = bool; + type Fetch<'w> = ChangeDetectionFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + item + } + + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> Self::Fetch<'w> { + Self::Fetch::<'w> { + registry: world.resource(), + storage: ChangeDetectionStorage::Uninit, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + Self::Fetch { + registry: fetch.registry, + storage: match fetch.storage { + ChangeDetectionStorage::Uninit => ChangeDetectionStorage::Uninit, + ChangeDetectionStorage::Table { ticks } => ChangeDetectionStorage::Table { ticks }, + ChangeDetectionStorage::SparseSet { components } => { + ChangeDetectionStorage::SparseSet { components } + } + }, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + // This will always be false for us, as we (so far) do not know at compile time whether the + // components our trait has been impl'd for are stored in table or in sparse set + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + _archetype: &'w Archetype, + table: &'w Table, + ) { + // Search for a registered trait impl that is present in the archetype. + // We check the table components first since it is faster to retrieve data of this type. + for &component in &*state.components { + if let Some(column) = table.get_column(component) { + fetch.storage = ChangeDetectionStorage::Table { + ticks: ThinSlicePtr::from(column.get_ticks_slice()), + }; + return; + } + } + for &component in &*state.components { + if let Some(components) = fetch.sparse_sets.get(component) { + fetch.storage = ChangeDetectionStorage::SparseSet { components }; + return; + } + } + // At least one of the components must be present in the table/sparse set. + debug_unreachable() + } + + #[inline] + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + // only gets called if IS_DENSE == true, which does not hold for us + debug_unreachable() + } + + #[inline(always)] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let ticks_ptr = match fetch.storage { + ChangeDetectionStorage::Uninit => { + // set_archetype must have been called already + debug_unreachable() + } + ChangeDetectionStorage::Table { ticks } => ticks.get(table_row), + ChangeDetectionStorage::SparseSet { components } => components + .get_ticks(entity) + .unwrap_or_else(|| debug_unreachable()), + }; + + (*ticks_ptr) + .deref() + .is_added(fetch.last_change_tick, fetch.change_tick) + } + + #[inline(always)] + unsafe fn filter_fetch(fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: usize) -> bool { + Self::fetch(fetch, entity, table_row) + } + + #[inline] + fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &Archetype, + access: &mut Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: read-only access +unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for OneAddedFilter<'a, Trait> {} + +pub struct OneChangedFilter<'a, Trait: ?Sized + TraitQuery> { + marker: PhantomData<&'a Trait>, +} + +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for OneChangedFilter<'a, Trait> { + type Item<'w> = bool; + type Fetch<'w> = ChangeDetectionFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { + item + } + + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> Self::Fetch<'w> { + Self::Fetch::<'w> { + registry: world.resource(), + storage: ChangeDetectionStorage::Uninit, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + Self::Fetch { + registry: fetch.registry, + storage: match fetch.storage { + ChangeDetectionStorage::Uninit => ChangeDetectionStorage::Uninit, + ChangeDetectionStorage::Table { ticks } => ChangeDetectionStorage::Table { ticks }, + ChangeDetectionStorage::SparseSet { components } => { + ChangeDetectionStorage::SparseSet { components } + } + }, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + // This will always be false for us, as we (so far) do not know at compile time whether the + // components our trait has been impl'd for are stored in table or in sparse set + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut Self::Fetch<'w>, + state: &Self::State, + _archetype: &'w Archetype, + table: &'w Table, + ) { + // Search for a registered trait impl that is present in the archetype. + // We check the table components first since it is faster to retrieve data of this type. + for &component in &*state.components { + if let Some(column) = table.get_column(component) { + fetch.storage = ChangeDetectionStorage::Table { + ticks: ThinSlicePtr::from(column.get_ticks_slice()), + }; + return; + } + } + for &component in &*state.components { + if let Some(components) = fetch.sparse_sets.get(component) { + fetch.storage = ChangeDetectionStorage::SparseSet { components }; + return; + } + } + // At least one of the components must be present in the table/sparse set. + debug_unreachable() + } + + #[inline] + unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) { + // only gets called if IS_DENSE == true, which does not hold for us + debug_unreachable() + } + + #[inline(always)] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + let ticks_ptr = match fetch.storage { + ChangeDetectionStorage::Uninit => { + // set_archetype must have been called already + debug_unreachable() + } + ChangeDetectionStorage::Table { ticks } => ticks.get(table_row), + ChangeDetectionStorage::SparseSet { components } => components + .get_ticks(entity) + .unwrap_or_else(|| debug_unreachable()), + }; + + (*ticks_ptr) + .deref() + .is_changed(fetch.last_change_tick, fetch.change_tick) + } + + #[inline(always)] + unsafe fn filter_fetch(fetch: &mut Self::Fetch<'_>, entity: Entity, table_row: usize) -> bool { + Self::fetch(fetch, entity, table_row) + } + + #[inline] + fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &Archetype, + access: &mut Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: read-only access +unsafe impl<'a, Trait: ?Sized + TraitQuery> ReadOnlyWorldQuery for OneChangedFilter<'a, Trait> {} diff --git a/src/lib.rs b/src/lib.rs index cf3de8c..6a28921 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,6 +189,56 @@ //! # bevy::ecs::system::assert_is_system(show_tooltips); //! ``` //! +//! Trait queries support basic change detection filtration. So to get all the components that +//! implement the target trait, and have also changed in some way since the last tick, you can: +//! ```ignore +//! fn show_tooltips( +//! tooltips_query: Query> +//! // ... +//! ) { +//! // Iterate over each entity that has tooltips, which have *changed* since the last tick +//! for entity_tooltips in &tooltips_query { +//! // Iterate over each component implementing `Tooltip` for the current entity. +//! for tooltip in entity_tooltips { +//! println!("Changed Tooltip: {}", tooltip.tooltip()); +//! } +//! } +//! } +//! ``` +//! +//! Similar to `ChangedAll`, we have `AddedAll`. +//! +//! If you know you have only one component that implements the target trait, you can use +//! `ChangedOne` (or `AddedOne`) filters, which returns an `Option`al entity if change was detected: +//! ```ignore +//! fn show_tooltips( +//! tooltips_query: Query> +//! // ... +//! ) { +//! // Iterate over each entity that has one tooltip implementing component +//! for maybe_changed_tooltip in &tooltips_query { +//! if let Some(changed_tooltip) = maybe_changed_tooltip { +//! println!("Changed Tooltip: {}", tooltip.tooltip()); +//! } +//! } +//! } +//! ``` +//! +//! or you can use `OneAddedFilter` or `OneChangedFilter` which behave more like the typical +//! `bevy` `Added/Changed` filters: +//! ```ignore +//! fn show_tooltips( +//! tooltips_query: Query, OneChangedFilter> +//! // ... +//! ) { +//! // Iterate over each entity that has one tooltip implementing component that has also changed +//! for changed_tooltip in &tooltips_query { +//! println!("Changed Tooltip: {}", tooltip.tooltip()); +//! } +//! } +//! ``` +//! Note in the above example how `OneChangedFilter` does *not* take a reference to the trait object! +//! //! # Performance //! //! The performance of trait queries is quite competitive. Here are some benchmarks for simple cases: diff --git a/src/one.rs b/src/one.rs index c098433..e713574 100644 --- a/src/one.rs +++ b/src/one.rs @@ -16,6 +16,8 @@ pub struct ReadTraitFetch<'w, Trait: ?Sized> { // After `Fetch::set_archetype` or `set_table` has been called, // this will carry the component data and metadata for the first trait impl found in the archetype. storage: ReadStorage<'w, Trait>, + last_change_tick: u32, + change_tick: u32, } enum ReadStorage<'w, Trait: ?Sized> { @@ -75,8 +77,20 @@ enum WriteStorage<'w, Trait: ?Sized> { /// [`WorldQuery`] adapter that fetches entities with exactly one component implementing a trait. pub struct One(pub T); +/// [`WorldQuery`] adapter that fetches entities with exactly one component implementing a trait, +/// with the condition that the component must also have changed since the last tick +pub struct ChangedOne(pub T); + +/// [`WorldQuery`] adapter that fetches entities with exactly one component implementing a trait, +/// with the condition that the component was newly added since the last tick +pub struct AddedOne(pub T); + unsafe impl<'a, T: ?Sized + TraitQuery> ReadOnlyWorldQuery for One<&'a T> {} +unsafe impl<'a, T: ?Sized + TraitQuery> ReadOnlyWorldQuery for ChangedOne<&'a T> {} + +unsafe impl<'a, T: ?Sized + TraitQuery> ReadOnlyWorldQuery for AddedOne<&'a T> {} + /// SAFETY: We only access the components registered in `DynQueryState`. /// This same set of components is used to match archetypes, and used to register world access. unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a Trait> { @@ -99,7 +113,9 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a Trait> { ) -> ReadTraitFetch<'w, Trait> { ReadTraitFetch { storage: ReadStorage::Uninit, + last_change_tick: 0, sparse_sets: &world.storages().sparse_sets, + change_tick: 0, } } @@ -121,7 +137,9 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a Trait> { ReadStorage::SparseSet { components, meta } } }, + last_change_tick: 0, sparse_sets: fetch.sparse_sets, + change_tick: 0, } } @@ -247,10 +265,10 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a Trait> { /// SAFETY: We only access the components registered in `DynQueryState`. /// This same set of components is used to match archetypes, and used to register world access. -unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a mut Trait> { - type Item<'w> = Mut<'w, Trait>; - type Fetch<'w> = WriteTraitFetch<'w, Trait>; - type ReadOnly = One<&'a Trait>; +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for ChangedOne<&'a Trait> { + type Item<'w> = Option<&'w Trait>; + type Fetch<'w> = ReadTraitFetch<'w, Trait>; + type ReadOnly = Self; type State = TraitQueryState; #[inline] @@ -264,35 +282,35 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a mut Trait> { _state: &Self::State, last_change_tick: u32, change_tick: u32, - ) -> WriteTraitFetch<'w, Trait> { - WriteTraitFetch { - storage: WriteStorage::Uninit, - sparse_sets: &world.storages().sparse_sets, + ) -> ReadTraitFetch<'w, Trait> { + ReadTraitFetch { + storage: ReadStorage::Uninit, last_change_tick, + sparse_sets: &world.storages().sparse_sets, change_tick, } } #[inline] unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { - WriteTraitFetch { + ReadTraitFetch { storage: match fetch.storage { - WriteStorage::Uninit => WriteStorage::Uninit, - WriteStorage::Table { + ReadStorage::Uninit => ReadStorage::Uninit, + ReadStorage::Table { column, + ticks, meta, - table_ticks, - } => WriteStorage::Table { + } => ReadStorage::Table { column, + ticks, meta, - table_ticks, }, - WriteStorage::SparseSet { components, meta } => { - WriteStorage::SparseSet { components, meta } + ReadStorage::SparseSet { components, meta } => { + ReadStorage::SparseSet { components, meta } } }, - sparse_sets: fetch.sparse_sets, last_change_tick: fetch.last_change_tick, + sparse_sets: fetch.sparse_sets, change_tick: fetch.change_tick, } } @@ -302,25 +320,27 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a mut Trait> { #[inline] unsafe fn set_archetype<'w>( - fetch: &mut WriteTraitFetch<'w, Trait>, + fetch: &mut ReadTraitFetch<'w, Trait>, state: &Self::State, _archetype: &'w bevy::ecs::archetype::Archetype, table: &'w bevy::ecs::storage::Table, ) { // Search for a registered trait impl that is present in the archetype. + // We check the table components first since it is faster to retrieve data of this type. for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { if let Some(column) = table.get_column(component) { - fetch.storage = WriteStorage::Table { + fetch.storage = ReadStorage::Table { column: column.get_data_ptr(), - table_ticks: column.get_ticks_slice().into(), + ticks: Some(ThinSlicePtr::from(column.get_ticks_slice())), meta, }; return; } } + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { if let Some(sparse_set) = fetch.sparse_sets.get(component) { - fetch.storage = WriteStorage::SparseSet { + fetch.storage = ReadStorage::SparseSet { components: sparse_set, meta, }; @@ -333,80 +353,846 @@ unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a mut Trait> { #[inline] unsafe fn set_table<'w>( - fetch: &mut WriteTraitFetch<'w, Trait>, + _fetch: &mut ReadTraitFetch<'w, Trait>, + _state: &Self::State, + _table: &'w bevy::ecs::storage::Table, + ) { + unimplemented!() + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + match fetch.storage { + // SAFETY: This function must have been called after `set_archetype`, + // so we know that `self.storage` has been initialized. + ReadStorage::Uninit => debug_unreachable(), + ReadStorage::Table { + column, + ticks, + meta, + } => ticks + .unwrap_or_else(|| debug_unreachable()) + .get(table_row) + .deref() + .is_changed(fetch.last_change_tick, fetch.change_tick) + .then(|| { + let ptr = column.byte_add(table_row * meta.size_bytes); + meta.dyn_ctor.cast(ptr) + }), + ReadStorage::SparseSet { components, meta } => { + let (ptr, ticks) = components + .get_with_ticks(entity) + .unwrap_or_else(|| debug_unreachable()); + ticks + .deref() + .is_changed(fetch.last_change_tick, fetch.change_tick) + .then(|| meta.dyn_ctor.cast(ptr)) + } + } + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in `DynQueryState`. +/// This same set of components is used to match archetypes, and used to register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for AddedOne<&'a Trait> { + type Item<'w> = Option<&'w Trait>; + type Fetch<'w> = ReadTraitFetch<'w, Trait>; + type ReadOnly = Self; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> ReadTraitFetch<'w, Trait> { + ReadTraitFetch { + storage: ReadStorage::Uninit, + last_change_tick, + sparse_sets: &world.storages().sparse_sets, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + ReadTraitFetch { + storage: match fetch.storage { + ReadStorage::Uninit => ReadStorage::Uninit, + ReadStorage::Table { + column, + ticks, + meta, + } => ReadStorage::Table { + column, + ticks, + meta, + }, + ReadStorage::SparseSet { components, meta } => { + ReadStorage::SparseSet { components, meta } + } + }, + last_change_tick: fetch.last_change_tick, + sparse_sets: fetch.sparse_sets, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut ReadTraitFetch<'w, Trait>, state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, table: &'w bevy::ecs::storage::Table, ) { - // Search for a registered trait impl that is present in the table. - for (&component, &meta) in std::iter::zip(&*state.components, &*state.meta) { + // Search for a registered trait impl that is present in the archetype. + // We check the table components first since it is faster to retrieve data of this type. + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { if let Some(column) = table.get_column(component) { - fetch.storage = WriteStorage::Table { + fetch.storage = ReadStorage::Table { column: column.get_data_ptr(), - table_ticks: column.get_ticks_slice().into(), + ticks: Some(ThinSlicePtr::from(column.get_ticks_slice())), meta, }; return; } } - // At least one of the components must be present in the table. + + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(sparse_set) = fetch.sparse_sets.get(component) { + fetch.storage = ReadStorage::SparseSet { + components: sparse_set, + meta, + }; + return; + } + } + // At least one of the components must be present in the table/sparse set. debug_unreachable() } + #[inline] + unsafe fn set_table<'w>( + _fetch: &mut ReadTraitFetch<'w, Trait>, + _state: &Self::State, + _table: &'w bevy::ecs::storage::Table, + ) { + unimplemented!() + } + #[inline] unsafe fn fetch<'w>( fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: usize, - ) -> Mut<'w, Trait> { - let dyn_ctor; - let (ptr, component_ticks) = match fetch.storage { + ) -> Self::Item<'w> { + match fetch.storage { // SAFETY: This function must have been called after `set_archetype`, // so we know that `self.storage` has been initialized. - WriteStorage::Uninit => debug_unreachable(), - WriteStorage::Table { + ReadStorage::Uninit => debug_unreachable(), + ReadStorage::Table { column, - table_ticks, + ticks, meta, - } => { - dyn_ctor = meta.dyn_ctor; - let ptr = column.byte_add(table_row * meta.size_bytes); - ( - // SAFETY: `column` allows for shared mutable access. - // So long as the caller does not invoke this function twice with the same archetype_index, - // this pointer will never be aliased. - ptr.assert_unique(), - // SAFETY: We have exclusive access to the component, so by extension - // we have exclusive access to the corresponding `ComponentTicks`. - table_ticks.get(table_row).deref_mut(), - ) - } - WriteStorage::SparseSet { components, meta } => { - dyn_ctor = meta.dyn_ctor; + } => ticks + .unwrap_or_else(|| debug_unreachable()) + .get(table_row) + .deref() + .is_added(fetch.last_change_tick, fetch.change_tick) + .then(|| { + let ptr = column.byte_add(table_row * meta.size_bytes); + meta.dyn_ctor.cast(ptr) + }), + ReadStorage::SparseSet { components, meta } => { let (ptr, ticks) = components .get_with_ticks(entity) .unwrap_or_else(|| debug_unreachable()); - ( - // SAFETY: We have exclusive access to the sparse set `components`. - // So long as the caller does not invoke this function twice with the same archetype_index, - // this pointer will never be aliased. - ptr.assert_unique(), - // SAFETY: We have exclusive access to the component, so by extension - // we have exclusive access to the corresponding `ComponentTicks`. - ticks.deref_mut(), - ) + ticks + .deref() + .is_added(fetch.last_change_tick, fetch.change_tick) + .then(|| meta.dyn_ctor.cast(ptr)) } - }; + } + } - Mut { - value: dyn_ctor.cast_mut(ptr), - ticks: Ticks { - component_ticks, - last_change_tick: fetch.last_change_tick, - change_tick: fetch.change_tick, - }, + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", + std::any::type_name::(), + ); + access.add_read(component); } } + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_read(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in `DynQueryState`. +/// This same set of components is used to match archetypes, and used to register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for One<&'a mut Trait> { + type Item<'w> = Mut<'w, Trait>; + type Fetch<'w> = WriteTraitFetch<'w, Trait>; + type ReadOnly = One<&'a Trait>; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> WriteTraitFetch<'w, Trait> { + WriteTraitFetch { + storage: WriteStorage::Uninit, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + WriteTraitFetch { + storage: match fetch.storage { + WriteStorage::Uninit => WriteStorage::Uninit, + WriteStorage::Table { + column, + meta, + table_ticks, + } => WriteStorage::Table { + column, + meta, + table_ticks, + }, + WriteStorage::SparseSet { components, meta } => { + WriteStorage::SparseSet { components, meta } + } + }, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the archetype. + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(sparse_set) = fetch.sparse_sets.get(component) { + fetch.storage = WriteStorage::SparseSet { + components: sparse_set, + meta, + }; + return; + } + } + // At least one of the components must be present in the table/sparse set. + debug_unreachable() + } + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the table. + for (&component, &meta) in std::iter::zip(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + // At least one of the components must be present in the table. + debug_unreachable() + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Mut<'w, Trait> { + let dyn_ctor; + let (ptr, component_ticks) = match fetch.storage { + // SAFETY: This function must have been called after `set_archetype`, + // so we know that `self.storage` has been initialized. + WriteStorage::Uninit => debug_unreachable(), + WriteStorage::Table { + column, + table_ticks, + meta, + } => { + dyn_ctor = meta.dyn_ctor; + let ptr = column.byte_add(table_row * meta.size_bytes); + ( + // SAFETY: `column` allows for shared mutable access. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + ptr.assert_unique(), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + table_ticks.get(table_row).deref_mut(), + ) + } + WriteStorage::SparseSet { components, meta } => { + dyn_ctor = meta.dyn_ctor; + let (ptr, ticks) = components + .get_with_ticks(entity) + .unwrap_or_else(|| debug_unreachable()); + ( + // SAFETY: We have exclusive access to the sparse set `components`. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + ptr.assert_unique(), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + ticks.deref_mut(), + ) + } + }; + + Mut { + value: dyn_ctor.cast_mut(ptr), + ticks: Ticks { + component_ticks, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + }, + } + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", + std::any::type_name::(), + ); + access.add_write(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_write(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in `DynQueryState`. +/// This same set of components is used to match archetypes, and used to register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for ChangedOne<&'a mut Trait> { + type Item<'w> = Option>; + type Fetch<'w> = WriteTraitFetch<'w, Trait>; + type ReadOnly = ChangedOne<&'a Trait>; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> WriteTraitFetch<'w, Trait> { + WriteTraitFetch { + storage: WriteStorage::Uninit, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + WriteTraitFetch { + storage: match fetch.storage { + WriteStorage::Uninit => WriteStorage::Uninit, + WriteStorage::Table { + column, + meta, + table_ticks, + } => WriteStorage::Table { + column, + meta, + table_ticks, + }, + WriteStorage::SparseSet { components, meta } => { + WriteStorage::SparseSet { components, meta } + } + }, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the archetype. + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(sparse_set) = fetch.sparse_sets.get(component) { + fetch.storage = WriteStorage::SparseSet { + components: sparse_set, + meta, + }; + return; + } + } + // At least one of the components must be present in the table/sparse set. + debug_unreachable() + } + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the table. + for (&component, &meta) in std::iter::zip(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + // At least one of the components must be present in the table. + debug_unreachable() + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + match fetch.storage { + // SAFETY: This function must have been called after `set_archetype`, + // so we know that `self.storage` has been initialized. + WriteStorage::Uninit => debug_unreachable(), + WriteStorage::Table { + column, + table_ticks, + meta, + } => { + table_ticks + .get(table_row) + .deref() + .is_changed(fetch.last_change_tick, fetch.change_tick) + .then(|| { + let ptr = column.byte_add(table_row * meta.size_bytes); + ( + // SAFETY: `column` allows for shared mutable access. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + meta.dyn_ctor.cast_mut(ptr.assert_unique()), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + table_ticks.get(table_row).deref_mut(), + ) + }) + } + WriteStorage::SparseSet { components, meta } => { + let (ptr, ticks) = components + .get_with_ticks(entity) + .unwrap_or_else(|| debug_unreachable()); + ticks + .deref() + .is_changed(fetch.last_change_tick, fetch.change_tick) + .then(|| { + ( + // SAFETY: We have exclusive access to the sparse set `components`. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + meta.dyn_ctor.cast_mut(ptr.assert_unique()), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + ticks.deref_mut(), + ) + }) + } + } + .map(|(value, component_ticks)| Mut { + value, + ticks: Ticks { + component_ticks, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + }, + }) + } + + #[inline] + fn update_component_access( + state: &Self::State, + access: &mut bevy::ecs::query::FilteredAccess, + ) { + for &component in &*state.components { + assert!( + !access.access().has_write(component), + "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", + std::any::type_name::(), + ); + access.add_write(component); + } + } + + #[inline] + fn update_archetype_component_access( + state: &Self::State, + archetype: &bevy::ecs::archetype::Archetype, + access: &mut bevy::ecs::query::Access, + ) { + for &component in &*state.components { + if let Some(archetype_component_id) = archetype.get_archetype_component_id(component) { + access.add_write(archetype_component_id); + } + } + } + + #[inline] + fn init_state(world: &mut World) -> Self::State { + TraitQueryState::init(world) + } + #[inline] + fn matches_component_set( + state: &Self::State, + set_contains_id: &impl Fn(ComponentId) -> bool, + ) -> bool { + state.matches_component_set_one(set_contains_id) + } +} + +/// SAFETY: We only access the components registered in `DynQueryState`. +/// This same set of components is used to match archetypes, and used to register world access. +unsafe impl<'a, Trait: ?Sized + TraitQuery> WorldQuery for AddedOne<&'a mut Trait> { + type Item<'w> = Option>; + type Fetch<'w> = WriteTraitFetch<'w, Trait>; + type ReadOnly = AddedOne<&'a Trait>; + type State = TraitQueryState; + + #[inline] + fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> { + item + } + + #[inline] + unsafe fn init_fetch<'w>( + world: &'w World, + _state: &Self::State, + last_change_tick: u32, + change_tick: u32, + ) -> WriteTraitFetch<'w, Trait> { + WriteTraitFetch { + storage: WriteStorage::Uninit, + sparse_sets: &world.storages().sparse_sets, + last_change_tick, + change_tick, + } + } + + #[inline] + unsafe fn clone_fetch<'w>(fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> { + WriteTraitFetch { + storage: match fetch.storage { + WriteStorage::Uninit => WriteStorage::Uninit, + WriteStorage::Table { + column, + meta, + table_ticks, + } => WriteStorage::Table { + column, + meta, + table_ticks, + }, + WriteStorage::SparseSet { components, meta } => { + WriteStorage::SparseSet { components, meta } + } + }, + sparse_sets: fetch.sparse_sets, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + } + } + + const IS_DENSE: bool = false; + const IS_ARCHETYPAL: bool = false; + + #[inline] + unsafe fn set_archetype<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + _archetype: &'w bevy::ecs::archetype::Archetype, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the archetype. + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + for (&component, &meta) in zip_exact(&*state.components, &*state.meta) { + if let Some(sparse_set) = fetch.sparse_sets.get(component) { + fetch.storage = WriteStorage::SparseSet { + components: sparse_set, + meta, + }; + return; + } + } + // At least one of the components must be present in the table/sparse set. + debug_unreachable() + } + + #[inline] + unsafe fn set_table<'w>( + fetch: &mut WriteTraitFetch<'w, Trait>, + state: &Self::State, + table: &'w bevy::ecs::storage::Table, + ) { + // Search for a registered trait impl that is present in the table. + for (&component, &meta) in std::iter::zip(&*state.components, &*state.meta) { + if let Some(column) = table.get_column(component) { + fetch.storage = WriteStorage::Table { + column: column.get_data_ptr(), + table_ticks: column.get_ticks_slice().into(), + meta, + }; + return; + } + } + // At least one of the components must be present in the table. + debug_unreachable() + } + + #[inline] + unsafe fn fetch<'w>( + fetch: &mut Self::Fetch<'w>, + entity: Entity, + table_row: usize, + ) -> Self::Item<'w> { + match fetch.storage { + // SAFETY: This function must have been called after `set_archetype`, + // so we know that `self.storage` has been initialized. + WriteStorage::Uninit => debug_unreachable(), + WriteStorage::Table { + column, + table_ticks, + meta, + } => { + table_ticks + .get(table_row) + .deref() + .is_added(fetch.last_change_tick, fetch.change_tick) + .then(|| { + let ptr = column.byte_add(table_row * meta.size_bytes); + ( + // SAFETY: `column` allows for shared mutable access. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + meta.dyn_ctor.cast_mut(ptr.assert_unique()), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + table_ticks.get(table_row).deref_mut(), + ) + }) + } + WriteStorage::SparseSet { components, meta } => { + let (ptr, ticks) = components + .get_with_ticks(entity) + .unwrap_or_else(|| debug_unreachable()); + ticks + .deref() + .is_added(fetch.last_change_tick, fetch.change_tick) + .then(|| { + ( + // SAFETY: We have exclusive access to the sparse set `components`. + // So long as the caller does not invoke this function twice with the same archetype_index, + // this pointer will never be aliased. + meta.dyn_ctor.cast_mut(ptr.assert_unique()), + // SAFETY: We have exclusive access to the component, so by extension + // we have exclusive access to the corresponding `ComponentTicks`. + ticks.deref_mut(), + ) + }) + } + } + .map(|(value, component_ticks)| Mut { + value, + ticks: Ticks { + component_ticks, + last_change_tick: fetch.last_change_tick, + change_tick: fetch.change_tick, + }, + }) + } + #[inline] fn update_component_access( state: &Self::State, diff --git a/src/tests.rs b/src/tests.rs index 7b3aa46..2a234e2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,5 @@ use super::*; +use crate::change_detection::{OneAddedFilter, OneChangedFilter}; use std::fmt::{Debug, Display}; #[derive(Resource, Default)] @@ -188,6 +189,323 @@ fn age_up_not(mut q: Query<&mut dyn Person, Without>) { } } +#[test] +fn added_all() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + world.spawn(Human("Henry".to_owned(), 22)); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_added_all_info) + .add_system(age_up_fem.after(print_added_all_info)); + + stage.run(&mut world); + + world.spawn((Human("Garbanzo".to_owned(), 17), Fem, Dolphin(17))); + + stage.run(&mut world); + + // only changes will occur now to the ages of Garbanzo/Reginald, so nothing should be printed + + stage.run(&mut world); + + println!("{:?}", world.resource::().0); + + assert_eq!( + world.resource::().0, + &[ + "Added people:", + "Henry: 22", + "", + "Added people:", + "Garbanzo: 17", + "Reginald: 17", + "", + "Added people:", + "" + ] + ); +} + +// Prints the name and age of every newly added `Person`. +fn print_added_all_info(people: Query>, mut output: ResMut) { + output.0.push("Added people:".to_string()); + for person in (&people).into_iter().flatten() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + +#[test] +fn changed_all() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_changed_all_info) + .add_system(age_up_fem.after(print_changed_all_info)); + + // Henry is newly added, so we expect him to be printed + world.spawn(Human("Henry".to_owned(), 22)); + + stage.run(&mut world); + + // Garbanzo and Dolphin (Reginald) are newly added, so we expect them to be printed + world.spawn((Human("Garbanzo".to_owned(), 17), Fem, Dolphin(17))); + + stage.run(&mut world); + + // Garbanzo and Dolphin (Reginald) will both be incremented in age by one by `age_up_fem`, so + // they should be printed again + + stage.run(&mut world); + + assert_eq!( + world.resource::().0, + &[ + "Changed people:", + "Henry: 22", + "", + "Changed people:", + "Garbanzo: 17", + "Reginald: 17", + "", + "Changed people:", + "Garbanzo: 18", + "Reginald: 18", + "", + ] + ); +} + +// Prints the name and age of every `Person` whose info has changed in some way +fn print_changed_all_info(people: Query>, mut output: ResMut) { + output.0.push("Changed people:".to_string()); + for person in (&people).into_iter().flatten() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + +#[test] +fn added_one() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + world.spawn(Human("Henry".to_owned(), 22)); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_added_one_info) + .add_system(age_up_fem.after(print_added_one_info)) + .add_system(age_up_not.after(print_added_one_info)); + + stage.run(&mut world); + + world.spawn((Dolphin(27), Fem)); + + stage.run(&mut world); + + stage.run(&mut world); + + assert_eq!( + world.resource::().0, + &[ + "Added people:", + "Henry: 22", + "", + "Added people:", + "Reginald: 27", + "", + "Added people:", + "", + ] + ); +} + +// Prints the name and age of every newly added `Person`. +fn print_added_one_info(people: Query>, mut output: ResMut) { + output.0.push("Added people:".to_string()); + for person in (&people).into_iter().flatten() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + +#[test] +fn changed_one() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + world.spawn(Human("Henry".to_owned(), 22)); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_changed_one_info) + .add_system(age_up_fem.after(print_changed_one_info)); + + stage.run(&mut world); + + world.spawn((Dolphin(27), Fem)); + + stage.run(&mut world); + + stage.run(&mut world); + + assert_eq!( + world.resource::().0, + &[ + "Changed people:", + "Henry: 22", + "", + "Changed people:", + "Reginald: 27", + "", + "Changed people:", + "Reginald: 28", + "" + ] + ); +} + +// Prints the name and age of every `Person` whose info has changed in some way +fn print_changed_one_info(people: Query>, mut output: ResMut) { + output.0.push("Changed people:".to_string()); + for person in (&people).into_iter().flatten() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + +#[test] +fn one_added_filter() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + world.spawn(Human("Henry".to_owned(), 22)); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_one_added_filter_info) + .add_system(age_up_fem.after(print_one_added_filter_info)) + .add_system(age_up_not.after(print_one_added_filter_info)); + + stage.run(&mut world); + + world.spawn((Dolphin(27), Fem)); + + stage.run(&mut world); + + stage.run(&mut world); + + assert_eq!( + world.resource::().0, + &[ + "Added people:", + "Henry: 22", + "", + "Added people:", + "Reginald: 27", + "", + "Added people:", + "", + ] + ); +} + +// Prints the name and age of every newly added `Person`. +fn print_one_added_filter_info( + people: Query, OneAddedFilter>, + mut output: ResMut, +) { + output.0.push("Added people:".to_string()); + for person in (&people).into_iter() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + +#[test] +fn one_changed_filter() { + let mut world = World::new(); + world.init_resource::(); + world + .register_component_as::() + .register_component_as::(); + + world.spawn(Human("Henry".to_owned(), 22)); + + let mut stage = SystemStage::parallel(); + stage + .add_system(print_one_changed_filter_info) + .add_system(age_up_fem.after(print_one_changed_filter_info)); + + stage.run(&mut world); + + world.spawn((Dolphin(27), Fem)); + + stage.run(&mut world); + + stage.run(&mut world); + + assert_eq!( + world.resource::().0, + &[ + "Changed people:", + "Henry: 22", + "", + "Changed people:", + "Reginald: 27", + "", + "Changed people:", + "Reginald: 28", + "" + ] + ); +} + +// Prints the name and age of every `Person` whose info has changed in some way +fn print_one_changed_filter_info( + people: Query, OneChangedFilter>, + mut output: ResMut, +) { + output.0.push("Changed people:".to_string()); + for person in (&people).into_iter() { + output + .0 + .push(format!("{}: {}", person.name(), person.age())); + } + output.0.push(default()); +} + #[queryable] pub trait Messages { fn send(&mut self, _: &dyn Display);