Increase type safety and clarity for change detection (#7905)

This commit is contained in:
JoJoJet 2023-03-09 12:17:02 -05:00 committed by GitHub
parent b7ac5d5121
commit 2e7b915ba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 475 additions and 570 deletions

View File

@ -224,16 +224,16 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
unsafe fn init_fetch<'__w>(
_world: &'__w #path::world::World,
state: &Self::State,
_last_change_tick: u32,
_change_tick: u32
_last_run: #path::component::Tick,
_this_run: #path::component::Tick,
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
#fetch_struct_name {
#(#field_idents:
<#field_types>::init_fetch(
_world,
&state.#field_idents,
_last_change_tick,
_change_tick
_last_run,
_this_run,
),
)*
#(#ignored_field_idents: Default::default(),)*

View File

@ -227,7 +227,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
state: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
ParamSet {
param_states: state,
@ -431,7 +431,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
state: &'s2 mut Self::State,
system_meta: &#path::system::SystemMeta,
world: &'w2 #path::world::World,
change_tick: u32,
change_tick: #path::component::Tick,
) -> Self::Item<'w2, 's2> {
let (#(#tuple_patterns,)*) = <
(#(#tuple_types,)*) as #path::system::SystemParam

View File

@ -283,7 +283,7 @@ impl BundleInfo {
components: &mut Components,
storages: &'a mut Storages,
archetype_id: ArchetypeId,
change_tick: u32,
change_tick: Tick,
) -> BundleInserter<'a, 'b> {
let new_archetype_id =
self.add_bundle_to_archetype(archetypes, storages, components, archetype_id);
@ -342,7 +342,7 @@ impl BundleInfo {
archetypes: &'a mut Archetypes,
components: &mut Components,
storages: &'a mut Storages,
change_tick: u32,
change_tick: Tick,
) -> BundleSpawner<'a, 'b> {
let new_archetype_id =
self.add_bundle_to_archetype(archetypes, storages, components, ArchetypeId::EMPTY);
@ -383,7 +383,7 @@ impl BundleInfo {
bundle_component_status: &S,
entity: Entity,
table_row: TableRow,
change_tick: u32,
change_tick: Tick,
bundle: T,
) {
// NOTE: get_components calls this closure on each component in "bundle order".
@ -397,7 +397,7 @@ impl BundleInfo {
// SAFETY: bundle_component is a valid index for this bundle
match bundle_component_status.get_status(bundle_component) {
ComponentStatus::Added => {
column.initialize(table_row, component_ptr, Tick::new(change_tick));
column.initialize(table_row, component_ptr, change_tick);
}
ComponentStatus::Mutated => {
column.replace(table_row, component_ptr, change_tick);
@ -508,7 +508,7 @@ pub(crate) struct BundleInserter<'a, 'b> {
sparse_sets: &'a mut SparseSets,
result: InsertBundleResult<'a>,
archetypes_ptr: *mut Archetype,
change_tick: u32,
change_tick: Tick,
}
pub(crate) enum InsertBundleResult<'a> {
@ -666,7 +666,7 @@ pub(crate) struct BundleSpawner<'a, 'b> {
bundle_info: &'b BundleInfo,
table: &'a mut Table,
sparse_sets: &'a mut SparseSets,
change_tick: u32,
change_tick: Tick,
}
impl<'a, 'b> BundleSpawner<'a, 'b> {

View File

@ -56,7 +56,7 @@ pub trait DetectChanges {
/// For comparison, the previous change tick of a system can be read using the
/// [`SystemChangeTick`](crate::system::SystemChangeTick)
/// [`SystemParam`](crate::system::SystemParam).
fn last_changed(&self) -> u32;
fn last_changed(&self) -> Tick;
}
/// Types that implement reliable change detection.
@ -109,7 +109,7 @@ pub trait DetectChangesMut: DetectChanges {
/// This is a complex and error-prone operation, primarily intended for use with rollback networking strategies.
/// If you merely want to flag this data as changed, use [`set_changed`](DetectChangesMut::set_changed) instead.
/// If you want to avoid triggering change detection, use [`bypass_change_detection`](DetectChangesMut::bypass_change_detection) instead.
fn set_last_changed(&mut self, last_change_tick: u32);
fn set_last_changed(&mut self, last_changed: Tick);
/// Manually bypasses change detection, allowing you to mutate the underlying value without updating the change tick.
///
@ -145,19 +145,19 @@ macro_rules! change_detection_impl {
fn is_added(&self) -> bool {
self.ticks
.added
.is_newer_than(self.ticks.last_change_tick, self.ticks.change_tick)
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn is_changed(&self) -> bool {
self.ticks
.changed
.is_newer_than(self.ticks.last_change_tick, self.ticks.change_tick)
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn last_changed(&self) -> u32 {
self.ticks.changed.tick
fn last_changed(&self) -> Tick {
*self.ticks.changed
}
}
@ -186,16 +186,12 @@ macro_rules! change_detection_mut_impl {
#[inline]
fn set_changed(&mut self) {
self.ticks
.changed
.set_changed(self.ticks.change_tick);
*self.ticks.changed = self.ticks.this_run;
}
#[inline]
fn set_last_changed(&mut self, last_changed: u32) {
self.ticks
.changed
.set_changed(last_changed);
fn set_last_changed(&mut self, last_changed: Tick) {
*self.ticks.changed = last_changed;
}
#[inline]
@ -242,8 +238,8 @@ macro_rules! impl_methods {
ticks: TicksMut {
added: self.ticks.added,
changed: self.ticks.changed,
last_change_tick: self.ticks.last_change_tick,
change_tick: self.ticks.change_tick,
last_run: self.ticks.last_run,
this_run: self.ticks.this_run,
}
}
}
@ -299,8 +295,8 @@ macro_rules! impl_debug {
pub(crate) struct Ticks<'a> {
pub(crate) added: &'a Tick,
pub(crate) changed: &'a Tick,
pub(crate) last_change_tick: u32,
pub(crate) change_tick: u32,
pub(crate) last_run: Tick,
pub(crate) this_run: Tick,
}
impl<'a> Ticks<'a> {
@ -309,14 +305,14 @@ impl<'a> Ticks<'a> {
#[inline]
pub(crate) unsafe fn from_tick_cells(
cells: TickCells<'a>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
Self {
added: cells.added.deref(),
changed: cells.changed.deref(),
last_change_tick,
change_tick,
last_run,
this_run,
}
}
}
@ -324,8 +320,8 @@ impl<'a> Ticks<'a> {
pub(crate) struct TicksMut<'a> {
pub(crate) added: &'a mut Tick,
pub(crate) changed: &'a mut Tick,
pub(crate) last_change_tick: u32,
pub(crate) change_tick: u32,
pub(crate) last_run: Tick,
pub(crate) this_run: Tick,
}
impl<'a> TicksMut<'a> {
@ -334,14 +330,14 @@ impl<'a> TicksMut<'a> {
#[inline]
pub(crate) unsafe fn from_tick_cells(
cells: TickCells<'a>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
Self {
added: cells.added.deref_mut(),
changed: cells.changed.deref_mut(),
last_change_tick,
change_tick,
last_run,
this_run,
}
}
}
@ -351,8 +347,8 @@ impl<'a> From<TicksMut<'a>> for Ticks<'a> {
Ticks {
added: ticks.added,
changed: ticks.changed,
last_change_tick: ticks.last_change_tick,
change_tick: ticks.change_tick,
last_run: ticks.last_run,
this_run: ticks.this_run,
}
}
}
@ -550,23 +546,23 @@ impl<'a, T: ?Sized> Mut<'a, T> {
/// - `last_changed` - A [`Tick`] that stores the last time the wrapped value was changed.
/// This will be updated to the value of `change_tick` if the returned smart pointer
/// is modified.
/// - `last_change_tick` - A [`Tick`], occurring before `change_tick`, which is used
/// - `last_run` - A [`Tick`], occurring before `this_run`, which is used
/// as a reference to determine whether the wrapped value is newly added or changed.
/// - `change_tick` - A [`Tick`] corresponding to the current point in time -- "now".
/// - `this_run` - A [`Tick`] corresponding to the current point in time -- "now".
pub fn new(
value: &'a mut T,
added: &'a mut Tick,
last_changed: &'a mut Tick,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
Self {
value,
ticks: TicksMut {
added,
changed: last_changed,
last_change_tick,
change_tick,
last_run,
this_run,
},
}
}
@ -643,8 +639,8 @@ impl<'a> MutUntyped<'a> {
ticks: TicksMut {
added: self.ticks.added,
changed: self.ticks.changed,
last_change_tick: self.ticks.last_change_tick,
change_tick: self.ticks.change_tick,
last_run: self.ticks.last_run,
this_run: self.ticks.this_run,
},
}
}
@ -681,19 +677,19 @@ impl<'a> DetectChanges for MutUntyped<'a> {
fn is_added(&self) -> bool {
self.ticks
.added
.is_newer_than(self.ticks.last_change_tick, self.ticks.change_tick)
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn is_changed(&self) -> bool {
self.ticks
.changed
.is_newer_than(self.ticks.last_change_tick, self.ticks.change_tick)
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn last_changed(&self) -> u32 {
self.ticks.changed.tick
fn last_changed(&self) -> Tick {
*self.ticks.changed
}
}
@ -702,12 +698,12 @@ impl<'a> DetectChangesMut for MutUntyped<'a> {
#[inline]
fn set_changed(&mut self) {
self.ticks.changed.set_changed(self.ticks.change_tick);
*self.ticks.changed = self.ticks.this_run;
}
#[inline]
fn set_last_changed(&mut self, last_changed: u32) {
self.ticks.changed.set_changed(last_changed);
fn set_last_changed(&mut self, last_changed: Tick) {
*self.ticks.changed = last_changed;
}
#[inline]
@ -792,7 +788,7 @@ mod tests {
}
let mut world = World::new();
world.last_change_tick = u32::MAX;
world.last_change_tick = Tick::new(u32::MAX);
*world.change_tick.get_mut() = 0;
// component added: 0, changed: 0
@ -820,8 +816,8 @@ mod tests {
let mut query = world.query::<Ref<C>>();
for tracker in query.iter(&world) {
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
let ticks_since_insert = change_tick.relative_to(*tracker.ticks.added).get();
let ticks_since_change = change_tick.relative_to(*tracker.ticks.changed).get();
assert!(ticks_since_insert > MAX_CHANGE_AGE);
assert!(ticks_since_change > MAX_CHANGE_AGE);
}
@ -830,8 +826,8 @@ mod tests {
world.check_change_ticks();
for tracker in query.iter(&world) {
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
let ticks_since_insert = change_tick.relative_to(*tracker.ticks.added).get();
let ticks_since_change = change_tick.relative_to(*tracker.ticks.changed).get();
assert!(ticks_since_insert == MAX_CHANGE_AGE);
assert!(ticks_since_change == MAX_CHANGE_AGE);
}
@ -846,8 +842,8 @@ mod tests {
let ticks = TicksMut {
added: &mut component_ticks.added,
changed: &mut component_ticks.changed,
last_change_tick: 3,
change_tick: 4,
last_run: Tick::new(3),
this_run: Tick::new(4),
};
let mut res = R {};
let res_mut = ResMut {
@ -856,10 +852,10 @@ mod tests {
};
let into_mut: Mut<R> = res_mut.into();
assert_eq!(1, into_mut.ticks.added.tick);
assert_eq!(2, into_mut.ticks.changed.tick);
assert_eq!(3, into_mut.ticks.last_change_tick);
assert_eq!(4, into_mut.ticks.change_tick);
assert_eq!(1, into_mut.ticks.added.get());
assert_eq!(2, into_mut.ticks.changed.get());
assert_eq!(3, into_mut.ticks.last_run.get());
assert_eq!(4, into_mut.ticks.this_run.get());
}
#[test]
@ -874,8 +870,8 @@ mod tests {
&mut res,
&mut component_ticks.added,
&mut component_ticks.changed,
2, // last_change_tick
4, // current change_tick
Tick::new(2), // last_run
Tick::new(4), // this_run
);
assert!(!val.is_added());
@ -891,8 +887,8 @@ mod tests {
let ticks = TicksMut {
added: &mut component_ticks.added,
changed: &mut component_ticks.changed,
last_change_tick: 3,
change_tick: 4,
last_run: Tick::new(3),
this_run: Tick::new(4),
};
let mut res = R {};
let non_send_mut = NonSendMut {
@ -901,10 +897,10 @@ mod tests {
};
let into_mut: Mut<R> = non_send_mut.into();
assert_eq!(1, into_mut.ticks.added.tick);
assert_eq!(2, into_mut.ticks.changed.tick);
assert_eq!(3, into_mut.ticks.last_change_tick);
assert_eq!(4, into_mut.ticks.change_tick);
assert_eq!(1, into_mut.ticks.added.get());
assert_eq!(2, into_mut.ticks.changed.get());
assert_eq!(3, into_mut.ticks.last_run.get());
assert_eq!(4, into_mut.ticks.this_run.get());
}
#[test]
@ -912,7 +908,8 @@ mod tests {
use super::*;
struct Outer(i64);
let (last_change_tick, change_tick) = (2, 3);
let last_run = Tick::new(2);
let this_run = Tick::new(3);
let mut component_ticks = ComponentTicks {
added: Tick::new(1),
changed: Tick::new(2),
@ -920,8 +917,8 @@ mod tests {
let ticks = TicksMut {
added: &mut component_ticks.added,
changed: &mut component_ticks.changed,
last_change_tick,
change_tick,
last_run,
this_run,
};
let mut outer = Outer(0);
@ -939,7 +936,7 @@ mod tests {
*inner = 64;
assert!(inner.is_changed());
// Modifying one field of a component should flag a change for the entire component.
assert!(component_ticks.is_changed(last_change_tick, change_tick));
assert!(component_ticks.is_changed(last_run, this_run));
}
#[test]

View File

@ -586,61 +586,72 @@ impl Components {
}
}
/// Used to track changes in state between system runs, e.g. components being added or accessed mutably.
/// A value that tracks when a system ran relative to other systems.
/// This is used to power change detection.
#[derive(Copy, Clone, Debug)]
pub struct Tick {
pub(crate) tick: u32,
tick: u32,
}
impl Tick {
/// The maximum relative age for a change tick.
/// The value of this is equal to [`crate::change_detection::MAX_CHANGE_AGE`].
///
/// Since change detection will not work for any ticks older than this,
/// ticks are periodically scanned to ensure their relative values are below this.
pub const MAX: Self = Self::new(MAX_CHANGE_AGE);
pub const fn new(tick: u32) -> Self {
Self { tick }
}
/// Gets the value of this change tick.
#[inline]
/// Returns `true` if this `Tick` occurred since the system's `last_change_tick`.
pub const fn get(self) -> u32 {
self.tick
}
/// Sets the value of this change tick.
#[inline]
pub fn set(&mut self, tick: u32) {
self.tick = tick;
}
#[inline]
/// Returns `true` if this `Tick` occurred since the system's `last_run`.
///
/// `change_tick` is the current tick of the system, used as a reference to help deal with wraparound.
pub fn is_newer_than(&self, last_change_tick: u32, change_tick: u32) -> bool {
// This works even with wraparound because the world tick (`change_tick`) is always "newer" than
// `last_change_tick` and `self.tick`, and we scan periodically to clamp `ComponentTicks` values
/// `this_run` is the current tick of the system, used as a reference to help deal with wraparound.
pub fn is_newer_than(self, last_run: Tick, this_run: Tick) -> bool {
// This works even with wraparound because the world tick (`this_run`) is always "newer" than
// `last_run` and `self.tick`, and we scan periodically to clamp `ComponentTicks` values
// so they never get older than `u32::MAX` (the difference would overflow).
//
// The clamp here ensures determinism (since scans could differ between app runs).
let ticks_since_insert = change_tick.wrapping_sub(self.tick).min(MAX_CHANGE_AGE);
let ticks_since_system = change_tick
.wrapping_sub(last_change_tick)
.min(MAX_CHANGE_AGE);
let ticks_since_insert = this_run.relative_to(self).tick.min(MAX_CHANGE_AGE);
let ticks_since_system = this_run.relative_to(last_run).tick.min(MAX_CHANGE_AGE);
ticks_since_system > ticks_since_insert
}
pub(crate) fn check_tick(&mut self, change_tick: u32) {
let age = change_tick.wrapping_sub(self.tick);
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true
// so long as this check always runs before that can happen.
if age > MAX_CHANGE_AGE {
self.tick = change_tick.wrapping_sub(MAX_CHANGE_AGE);
}
/// Returns a change tick representing the relationship between `self` and `other`.
pub(crate) fn relative_to(self, other: Self) -> Self {
let tick = self.tick.wrapping_sub(other.tick);
Self { tick }
}
/// Manually sets the change tick.
/// Wraps this change tick's value if it exceeds [`Tick::MAX`].
///
/// This is normally done automatically via the [`DerefMut`](std::ops::DerefMut) implementation
/// on [`Mut<T>`](crate::change_detection::Mut), [`ResMut<T>`](crate::change_detection::ResMut), etc.
/// However, components and resources that make use of interior mutability might require manual updates.
///
/// # Example
/// ```rust,no_run
/// # use bevy_ecs::{world::World, component::ComponentTicks};
/// let world: World = unimplemented!();
/// let component_ticks: ComponentTicks = unimplemented!();
///
/// component_ticks.set_changed(world.read_change_tick());
/// ```
#[inline]
pub fn set_changed(&mut self, change_tick: u32) {
self.tick = change_tick;
/// Returns `true` if wrapping was performed. Otherwise, returns `false`.
pub(crate) fn check_tick(&mut self, tick: Tick) -> bool {
let age = tick.relative_to(*self);
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true
// so long as this check always runs before that can happen.
if age.get() > Self::MAX.get() {
*self = tick.relative_to(Self::MAX);
true
} else {
false
}
}
}
@ -673,20 +684,20 @@ pub struct ComponentTicks {
impl ComponentTicks {
#[inline]
/// Returns `true` if the component was added after the system last ran.
pub fn is_added(&self, last_change_tick: u32, change_tick: u32) -> bool {
self.added.is_newer_than(last_change_tick, change_tick)
pub fn is_added(&self, last_run: Tick, this_run: Tick) -> bool {
self.added.is_newer_than(last_run, this_run)
}
#[inline]
/// Returns `true` if the component was added or mutably dereferenced after the system last ran.
pub fn is_changed(&self, last_change_tick: u32, change_tick: u32) -> bool {
self.changed.is_newer_than(last_change_tick, change_tick)
pub fn is_changed(&self, last_run: Tick, this_run: Tick) -> bool {
self.changed.is_newer_than(last_run, this_run)
}
pub(crate) fn new(change_tick: u32) -> Self {
pub(crate) fn new(change_tick: Tick) -> Self {
Self {
added: Tick::new(change_tick),
changed: Tick::new(change_tick),
added: change_tick,
changed: change_tick,
}
}
@ -705,8 +716,8 @@ impl ComponentTicks {
/// component_ticks.set_changed(world.read_change_tick());
/// ```
#[inline]
pub fn set_changed(&mut self, change_tick: u32) {
self.changed.set_changed(change_tick);
pub fn set_changed(&mut self, change_tick: Tick) {
self.changed = change_tick;
}
}

View File

@ -329,8 +329,8 @@ pub unsafe trait WorldQuery {
unsafe fn init_fetch<'w>(
world: &'w World,
state: &Self::State,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w>;
/// While this function can be called for any query, it is always safe to call if `Self: ReadOnlyWorldQuery` holds.
@ -460,8 +460,8 @@ unsafe impl WorldQuery for Entity {
unsafe fn init_fetch<'w>(
_world: &'w World,
_state: &Self::State,
_last_change_tick: u32,
_change_tick: u32,
_last_run: Tick,
_this_run: Tick,
) -> Self::Fetch<'w> {
}
@ -542,8 +542,8 @@ unsafe impl<T: Component> WorldQuery for &T {
unsafe fn init_fetch<'w>(
world: &'w World,
&component_id: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
_last_run: Tick,
_this_run: Tick,
) -> ReadFetch<'w, T> {
ReadFetch {
table_components: None,
@ -660,8 +660,8 @@ pub struct RefFetch<'w, T> {
// T::Storage = SparseStorage
sparse_set: Option<&'w ComponentSparseSet>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
/// SAFETY: `Self` is the same as `Self::ReadOnly`
@ -687,8 +687,8 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
unsafe fn init_fetch<'w>(
world: &'w World,
&component_id: &ComponentId,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> RefFetch<'w, T> {
RefFetch {
table_data: None,
@ -699,8 +699,8 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
.get(component_id)
.debug_checked_unwrap()
}),
last_change_tick,
change_tick,
last_run,
this_run,
}
}
@ -708,8 +708,8 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
RefFetch {
table_data: fetch.table_data,
sparse_set: fetch.sparse_set,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
}
}
@ -754,8 +754,8 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
ticks: Ticks {
added: added_ticks.get(table_row.index()).deref(),
changed: changed_ticks.get(table_row.index()).deref(),
change_tick: fetch.change_tick,
last_change_tick: fetch.last_change_tick,
this_run: fetch.this_run,
last_run: fetch.last_run,
},
}
}
@ -767,7 +767,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
.debug_checked_unwrap();
Ref {
value: component.deref(),
ticks: Ticks::from_tick_cells(ticks, fetch.last_change_tick, fetch.change_tick),
ticks: Ticks::from_tick_cells(ticks, fetch.last_run, fetch.this_run),
}
}
}
@ -821,8 +821,8 @@ pub struct WriteFetch<'w, T> {
// T::Storage = SparseStorage
sparse_set: Option<&'w ComponentSparseSet>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
/// SAFETY: access of `&T` is a subset of `&mut T`
@ -848,8 +848,8 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
unsafe fn init_fetch<'w>(
world: &'w World,
&component_id: &ComponentId,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> WriteFetch<'w, T> {
WriteFetch {
table_data: None,
@ -860,8 +860,8 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
.get(component_id)
.debug_checked_unwrap()
}),
last_change_tick,
change_tick,
last_run,
this_run,
}
}
@ -869,8 +869,8 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
WriteFetch {
table_data: fetch.table_data,
sparse_set: fetch.sparse_set,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
}
}
@ -915,8 +915,8 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
ticks: TicksMut {
added: added_ticks.get(table_row.index()).deref_mut(),
changed: changed_ticks.get(table_row.index()).deref_mut(),
change_tick: fetch.change_tick,
last_change_tick: fetch.last_change_tick,
this_run: fetch.this_run,
last_run: fetch.last_run,
},
}
}
@ -928,11 +928,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
.debug_checked_unwrap();
Mut {
value: component.assert_unique().deref_mut(),
ticks: TicksMut::from_tick_cells(
ticks,
fetch.last_change_tick,
fetch.change_tick,
),
ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run),
}
}
}
@ -996,11 +992,11 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
unsafe fn init_fetch<'w>(
world: &'w World,
state: &T::State,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> OptionFetch<'w, T> {
OptionFetch {
fetch: T::init_fetch(world, state, last_change_tick, change_tick),
fetch: T::init_fetch(world, state, last_run, this_run),
matches: false,
}
}
@ -1114,8 +1110,8 @@ unsafe impl<T: ReadOnlyWorldQuery> ReadOnlyWorldQuery for Option<T> {}
#[deprecated = "`ChangeTrackers<T>` will be removed in bevy 0.11. Use `bevy_ecs::prelude::Ref<T>` instead."]
pub struct ChangeTrackers<T: Component> {
pub(crate) component_ticks: ComponentTicks,
pub(crate) last_change_tick: u32,
pub(crate) change_tick: u32,
pub(crate) last_run: Tick,
pub(crate) this_run: Tick,
marker: PhantomData<T>,
}
@ -1134,8 +1130,8 @@ impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChangeTrackers")
.field("component_ticks", &self.component_ticks)
.field("last_change_tick", &self.last_change_tick)
.field("change_tick", &self.change_tick)
.field("last_run", &self.last_run)
.field("this_run", &self.this_run)
.finish()
}
}
@ -1144,14 +1140,13 @@ impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
impl<T: Component> ChangeTrackers<T> {
/// Returns true if this component has been added since the last execution of this system.
pub fn is_added(&self) -> bool {
self.component_ticks
.is_added(self.last_change_tick, self.change_tick)
self.component_ticks.is_added(self.last_run, self.this_run)
}
/// Returns true if this component has been changed since the last execution of this system.
pub fn is_changed(&self) -> bool {
self.component_ticks
.is_changed(self.last_change_tick, self.change_tick)
.is_changed(self.last_run, self.this_run)
}
}
@ -1164,8 +1159,8 @@ pub struct ChangeTrackersFetch<'w, T> {
sparse_set: Option<&'w ComponentSparseSet>,
marker: PhantomData<T>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
#[allow(deprecated)]
@ -1192,8 +1187,8 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
unsafe fn init_fetch<'w>(
world: &'w World,
&component_id: &ComponentId,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> ChangeTrackersFetch<'w, T> {
ChangeTrackersFetch {
table_added: None,
@ -1206,8 +1201,8 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
.debug_checked_unwrap()
}),
marker: PhantomData,
last_change_tick,
change_tick,
last_run,
this_run,
}
}
@ -1217,8 +1212,8 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
table_changed: fetch.table_changed,
sparse_set: fetch.sparse_set,
marker: fetch.marker,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
}
}
@ -1268,8 +1263,8 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
}
},
marker: PhantomData,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
},
StorageType::SparseSet => ChangeTrackers {
component_ticks: fetch
@ -1278,8 +1273,8 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
.get_ticks(entity)
.debug_checked_unwrap(),
marker: PhantomData,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
},
}
}
@ -1338,9 +1333,9 @@ macro_rules! impl_tuple_fetch {
}
#[allow(clippy::unused_unit)]
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
let ($($name,)*) = state;
($($name::init_fetch(_world, $name, _last_change_tick, _change_tick),)*)
($($name::init_fetch(_world, $name, _last_run, _this_run),)*)
}
unsafe fn clone_fetch<'w>(
@ -1447,9 +1442,9 @@ macro_rules! impl_anytuple_fetch {
}
#[allow(clippy::unused_unit)]
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
let ($($name,)*) = state;
($(($name::init_fetch(_world, $name, _last_change_tick, _change_tick), false),)*)
($(($name::init_fetch(_world, $name, _last_run, _this_run), false),)*)
}
unsafe fn clone_fetch<'w>(
@ -1585,13 +1580,7 @@ unsafe impl<Q: WorldQuery> WorldQuery for NopWorldQuery<Q> {
const IS_ARCHETYPAL: bool = true;
#[inline(always)]
unsafe fn init_fetch(
_world: &World,
_state: &Q::State,
_last_change_tick: u32,
_change_tick: u32,
) {
}
unsafe fn init_fetch(_world: &World, _state: &Q::State, _last_run: Tick, _this_run: Tick) {}
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}

View File

@ -50,13 +50,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
unsafe fn init_fetch(
_world: &World,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) {
}
unsafe fn init_fetch(_world: &World, _state: &ComponentId, _last_run: Tick, _this_run: Tick) {}
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
@ -152,13 +146,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
unsafe fn init_fetch(
_world: &World,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) {
}
unsafe fn init_fetch(_world: &World, _state: &ComponentId, _last_run: Tick, _this_run: Tick) {}
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
@ -277,10 +265,10 @@ macro_rules! impl_query_filter_tuple {
const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*;
unsafe fn init_fetch<'w>(world: &'w World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w>(world: &'w World, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
let ($($filter,)*) = state;
($(OrFetch {
fetch: $filter::init_fetch(world, $filter, last_change_tick, change_tick),
fetch: $filter::init_fetch(world, $filter, last_run, this_run),
matches: false,
},)*)
}
@ -417,8 +405,8 @@ macro_rules! impl_tick_filter {
table_ticks: Option< ThinSlicePtr<'w, UnsafeCell<Tick>>>,
marker: PhantomData<T>,
sparse_set: Option<&'w ComponentSparseSet>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
// SAFETY: `Self::ReadOnly` is the same as `Self`
@ -432,7 +420,7 @@ macro_rules! impl_tick_filter {
item
}
unsafe fn init_fetch<'w>(world: &'w World, &id: &ComponentId, last_change_tick: u32, change_tick: u32) -> Self::Fetch<'w> {
unsafe fn init_fetch<'w>(world: &'w World, &id: &ComponentId, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
Self::Fetch::<'w> {
table_ticks: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
@ -443,8 +431,8 @@ macro_rules! impl_tick_filter {
.debug_checked_unwrap()
}),
marker: PhantomData,
last_change_tick,
change_tick,
last_run,
this_run,
}
}
@ -454,8 +442,8 @@ macro_rules! impl_tick_filter {
$fetch_name {
table_ticks: fetch.table_ticks,
sparse_set: fetch.sparse_set,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
last_run: fetch.last_run,
this_run: fetch.this_run,
marker: PhantomData,
}
}
@ -509,7 +497,7 @@ macro_rules! impl_tick_filter {
.debug_checked_unwrap()
.get(table_row.index())
.deref()
.is_newer_than(fetch.last_change_tick, fetch.change_tick)
.is_newer_than(fetch.last_run, fetch.this_run)
}
StorageType::SparseSet => {
let sparse_set = &fetch
@ -518,7 +506,7 @@ macro_rules! impl_tick_filter {
$get_sparse_set(sparse_set, entity)
.debug_checked_unwrap()
.deref()
.is_newer_than(fetch.last_change_tick, fetch.change_tick)
.is_newer_than(fetch.last_run, fetch.this_run)
}
}
}

View File

@ -1,5 +1,6 @@
use crate::{
archetype::{ArchetypeEntity, ArchetypeId, Archetypes},
component::Tick,
entity::{Entities, Entity},
prelude::World,
query::{ArchetypeFilter, DebugCheckedUnwrap, QueryState, WorldQuery},
@ -29,14 +30,14 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIter<'w, 's, Q, F> {
pub(crate) unsafe fn new(
world: &'w World,
query_state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
QueryIter {
query_state,
tables: &world.storages().tables,
archetypes: &world.archetypes,
cursor: QueryIterationCursor::init(world, query_state, last_change_tick, change_tick),
cursor: QueryIterationCursor::init(world, query_state, last_run, this_run),
}
}
}
@ -98,21 +99,11 @@ where
world: &'w World,
query_state: &'s QueryState<Q, F>,
entity_list: EntityList,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> QueryManyIter<'w, 's, Q, F, I> {
let fetch = Q::init_fetch(
world,
&query_state.fetch_state,
last_change_tick,
change_tick,
);
let filter = F::init_fetch(
world,
&query_state.filter_state,
last_change_tick,
change_tick,
);
let fetch = Q::init_fetch(world, &query_state.fetch_state, last_run, this_run);
let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run);
QueryManyIter {
query_state,
entities: &world.entities,
@ -298,8 +289,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize>
pub(crate) unsafe fn new(
world: &'w World,
query_state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
// Initialize array with cursors.
// There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit
@ -312,16 +303,16 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize>
ptr.write(QueryIterationCursor::init(
world,
query_state,
last_change_tick,
change_tick,
last_run,
this_run,
));
}
for slot in (1..K).map(|offset| ptr.add(offset)) {
slot.write(QueryIterationCursor::init_empty(
world,
query_state,
last_change_tick,
change_tick,
last_run,
this_run,
));
}
@ -496,34 +487,24 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIterationCursor<'w, 's,
unsafe fn init_empty(
world: &'w World,
query_state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
QueryIterationCursor {
table_id_iter: [].iter(),
archetype_id_iter: [].iter(),
..Self::init(world, query_state, last_change_tick, change_tick)
..Self::init(world, query_state, last_run, this_run)
}
}
unsafe fn init(
world: &'w World,
query_state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Self {
let fetch = Q::init_fetch(
world,
&query_state.fetch_state,
last_change_tick,
change_tick,
);
let filter = F::init_fetch(
world,
&query_state.filter_state,
last_change_tick,
change_tick,
);
let fetch = Q::init_fetch(world, &query_state.fetch_state, last_run, this_run);
let filter = F::init_fetch(world, &query_state.filter_state, last_run, this_run);
QueryIterationCursor {
fetch,
filter,

View File

@ -1,6 +1,6 @@
use crate::{
archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
component::ComponentId,
component::{ComponentId, Tick},
entity::Entity,
prelude::FromWorld,
query::{
@ -130,11 +130,11 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
#[inline]
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
pub fn is_empty(&self, world: &World, last_run: Tick, this_run: Tick) -> bool {
// SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access
unsafe {
self.as_nop()
.iter_unchecked_manual(world, last_change_tick, change_tick)
.iter_unchecked_manual(world, last_run, this_run)
.next()
.is_none()
}
@ -390,8 +390,8 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
&self,
world: &'w World,
entity: Entity,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Result<Q::Item<'w>, QueryEntityError> {
let location = world
.entities
@ -407,8 +407,8 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
.archetypes
.get(location.archetype_id)
.debug_checked_unwrap();
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = F::init_fetch(world, &self.filter_state, last_change_tick, change_tick);
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);
let table = world
.storages()
@ -436,20 +436,17 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
&self,
world: &'w World,
entities: [Entity; N],
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
let mut values = [(); N].map(|_| MaybeUninit::uninit());
for (value, entity) in std::iter::zip(&mut values, entities) {
// SAFETY: fetch is read-only
// and world must be validated
let item = self.as_readonly().get_unchecked_manual(
world,
entity,
last_change_tick,
change_tick,
)?;
let item = self
.as_readonly()
.get_unchecked_manual(world, entity, last_run, this_run)?;
*value = MaybeUninit::new(item);
}
@ -471,8 +468,8 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
&self,
world: &'w World,
entities: [Entity; N],
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Result<[Q::Item<'w>; N], QueryEntityError> {
// Verify that all entities are unique
for i in 0..N {
@ -486,7 +483,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
let mut values = [(); N].map(|_| MaybeUninit::uninit());
for (value, entity) in std::iter::zip(&mut values, entities) {
let item = self.get_unchecked_manual(world, entity, last_change_tick, change_tick)?;
let item = self.get_unchecked_manual(world, entity, last_run, this_run)?;
*value = MaybeUninit::new(item);
}
@ -708,10 +705,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
pub(crate) unsafe fn iter_unchecked_manual<'w, 's>(
&'s self,
world: &'w World,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> QueryIter<'w, 's, Q, F> {
QueryIter::new(world, self, last_change_tick, change_tick)
QueryIter::new(world, self, last_run, this_run)
}
/// Returns an [`Iterator`] for the given [`World`] and list of [`Entity`]'s, where the last change and
@ -729,13 +726,13 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
&'s self,
entities: EntityList,
world: &'w World,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> QueryManyIter<'w, 's, Q, F, EntityList::IntoIter>
where
EntityList::Item: Borrow<Entity>,
{
QueryManyIter::new(world, self, entities, last_change_tick, change_tick)
QueryManyIter::new(world, self, entities, last_run, this_run)
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
@ -752,10 +749,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
pub(crate) unsafe fn iter_combinations_unchecked_manual<'w, 's, const K: usize>(
&'s self,
world: &'w World,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> QueryCombinationIter<'w, 's, Q, F, K> {
QueryCombinationIter::new(world, self, last_change_tick, change_tick)
QueryCombinationIter::new(world, self, last_run, this_run)
}
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
@ -856,13 +853,13 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
&self,
world: &'w World,
mut func: FN,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) {
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = F::init_fetch(world, &self.filter_state, last_change_tick, change_tick);
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);
let tables = &world.storages().tables;
if Q::IS_DENSE && F::IS_DENSE {
@ -931,8 +928,8 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
world: &'w World,
batch_size: usize,
func: FN,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) {
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
@ -950,18 +947,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
let func = func.clone();
let len = batch_size.min(table.entity_count() - offset);
let task = async move {
let mut fetch = Q::init_fetch(
world,
&self.fetch_state,
last_change_tick,
change_tick,
);
let mut filter = F::init_fetch(
world,
&self.filter_state,
last_change_tick,
change_tick,
);
let mut fetch =
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter =
F::init_fetch(world, &self.filter_state, last_run, this_run);
let tables = &world.storages().tables;
let table = tables.get(*table_id).debug_checked_unwrap();
let entities = table.entities();
@ -1002,18 +991,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
let func = func.clone();
let len = batch_size.min(archetype.len() - offset);
let task = async move {
let mut fetch = Q::init_fetch(
world,
&self.fetch_state,
last_change_tick,
change_tick,
);
let mut filter = F::init_fetch(
world,
&self.filter_state,
last_change_tick,
change_tick,
);
let mut fetch =
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
let mut filter =
F::init_fetch(world, &self.filter_state, last_run, this_run);
let tables = &world.storages().tables;
let archetype =
world.archetypes.get(*archetype_id).debug_checked_unwrap();
@ -1161,10 +1142,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
pub unsafe fn get_single_unchecked_manual<'w>(
&self,
world: &'w World,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
) -> Result<Q::Item<'w>, QuerySingleError> {
let mut query = self.iter_unchecked_manual(world, last_change_tick, change_tick);
let mut query = self.iter_unchecked_manual(world, last_run, this_run);
let first = query.next();
let extra = query.next().is_some();

View File

@ -2,7 +2,7 @@
use crate::{
self as bevy_ecs,
component::{Component, ComponentId, ComponentIdFor},
component::{Component, ComponentId, ComponentIdFor, Tick},
entity::Entity,
event::{EventId, Events, ManualEventIterator, ManualEventIteratorWithId, ManualEventReader},
prelude::Local,
@ -265,7 +265,7 @@ unsafe impl<'a> SystemParam for &'a RemovedComponentEvents {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world.removed_components()
}

View File

@ -17,7 +17,7 @@ use fixedbitset::FixedBitSet;
use crate::{
self as bevy_ecs,
component::{ComponentId, Components},
component::{ComponentId, Components, Tick},
schedule::*,
system::{BoxedSystem, Resource, System},
world::World,
@ -99,7 +99,7 @@ impl Schedules {
/// Iterates the change ticks of all systems in all stored schedules and clamps any older than
/// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
/// This prevents overflow and thus prevents false positives.
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
#[cfg(feature = "trace")]
let _all_span = info_span!("check stored schedule ticks").entered();
// label used when trace feature is enabled
@ -283,7 +283,7 @@ impl Schedule {
/// Iterates the change ticks of all systems in the schedule and clamps any older than
/// [`MAX_CHANGE_AGE`](crate::change_detection::MAX_CHANGE_AGE).
/// This prevents overflow and thus prevents false positives.
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for system in &mut self.executable.systems {
system.check_change_tick(change_tick);
}

View File

@ -1,6 +1,6 @@
use crate::archetype::ArchetypeComponentId;
use crate::change_detection::{MutUntyped, TicksMut};
use crate::component::{ComponentId, ComponentTicks, Components, TickCells};
use crate::component::{ComponentId, ComponentTicks, Components, Tick, TickCells};
use crate::storage::{Column, SparseSet, TableRow};
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
use std::{mem::ManuallyDrop, thread::ThreadId};
@ -111,17 +111,13 @@ impl<const SEND: bool> ResourceData<SEND> {
/// # Panics
/// If `SEND` is false, this will panic if a value is present and is not accessed from the
/// original thread it was inserted in.
pub(crate) fn get_mut(
&mut self,
last_change_tick: u32,
change_tick: u32,
) -> Option<MutUntyped<'_>> {
pub(crate) fn get_mut(&mut self, last_run: Tick, this_run: Tick) -> Option<MutUntyped<'_>> {
let (ptr, ticks) = self.get_with_ticks()?;
Some(MutUntyped {
// SAFETY: We have exclusive access to the underlying storage.
value: unsafe { ptr.assert_unique() },
// SAFETY: We have exclusive access to the underlying storage.
ticks: unsafe { TicksMut::from_tick_cells(ticks, last_change_tick, change_tick) },
ticks: unsafe { TicksMut::from_tick_cells(ticks, last_run, this_run) },
})
}
@ -135,7 +131,7 @@ impl<const SEND: bool> ResourceData<SEND> {
/// # Safety
/// - `value` must be valid for the underlying type for the resource.
#[inline]
pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: u32) {
pub(crate) unsafe fn insert(&mut self, value: OwningPtr<'_>, change_tick: Tick) {
if self.is_present() {
self.validate_access();
self.column.replace(Self::ROW, value, change_tick);
@ -284,7 +280,7 @@ impl<const SEND: bool> Resources<SEND> {
})
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for info in self.resources.values_mut() {
info.column.check_change_ticks(change_tick);
}

View File

@ -163,7 +163,12 @@ impl ComponentSparseSet {
/// # Safety
/// The `value` pointer must point to a valid address that matches the [`Layout`](std::alloc::Layout)
/// inside the [`ComponentInfo`] given when constructing this sparse set.
pub(crate) unsafe fn insert(&mut self, entity: Entity, value: OwningPtr<'_>, change_tick: u32) {
pub(crate) unsafe fn insert(
&mut self,
entity: Entity,
value: OwningPtr<'_>,
change_tick: Tick,
) {
if let Some(&dense_index) = self.sparse.get(entity.index()) {
#[cfg(debug_assertions)]
assert_eq!(entity, self.entities[dense_index as usize]);
@ -332,7 +337,7 @@ impl ComponentSparseSet {
}
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
self.dense.check_change_ticks(change_tick);
}
}
@ -614,7 +619,7 @@ impl SparseSets {
}
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for set in self.sets.values_mut() {
set.check_change_ticks(change_tick);
}

View File

@ -152,13 +152,10 @@ impl Column {
/// # Safety
/// Assumes data has already been allocated for the given row.
#[inline]
pub(crate) unsafe fn replace(&mut self, row: TableRow, data: OwningPtr<'_>, change_tick: u32) {
pub(crate) unsafe fn replace(&mut self, row: TableRow, data: OwningPtr<'_>, change_tick: Tick) {
debug_assert!(row.index() < self.len());
self.data.replace_unchecked(row.index(), data);
self.changed_ticks
.get_unchecked_mut(row.index())
.get_mut()
.set_changed(change_tick);
*self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
}
/// Writes component data to the column at given row.
@ -495,7 +492,7 @@ impl Column {
}
#[inline]
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for component_ticks in &mut self.added_ticks {
component_ticks.get_mut().check_tick(change_tick);
}
@ -770,7 +767,7 @@ impl Table {
self.entities.is_empty()
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for column in self.columns.values_mut() {
column.check_change_ticks(change_tick);
}
@ -889,7 +886,7 @@ impl Tables {
}
}
pub(crate) fn check_change_ticks(&mut self, change_tick: u32) {
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
for table in &mut self.tables {
table.check_change_ticks(change_tick);
}

View File

@ -3,7 +3,10 @@ use std::{borrow::Cow, cell::UnsafeCell, marker::PhantomData};
use bevy_ptr::UnsafeCellDeref;
use crate::{
archetype::ArchetypeComponentId, component::ComponentId, prelude::World, query::Access,
archetype::ArchetypeComponentId,
component::{ComponentId, Tick},
prelude::World,
query::Access,
};
use super::{ReadOnlySystem, System};
@ -203,18 +206,18 @@ where
.extend(self.b.archetype_component_access());
}
fn check_change_tick(&mut self, change_tick: u32) {
fn check_change_tick(&mut self, change_tick: Tick) {
self.a.check_change_tick(change_tick);
self.b.check_change_tick(change_tick);
}
fn get_last_change_tick(&self) -> u32 {
self.a.get_last_change_tick()
fn get_last_run(&self) -> Tick {
self.a.get_last_run()
}
fn set_last_change_tick(&mut self, last_change_tick: u32) {
self.a.set_last_change_tick(last_change_tick);
self.b.set_last_change_tick(last_change_tick);
fn set_last_run(&mut self, last_run: Tick) {
self.a.set_last_run(last_run);
self.b.set_last_run(last_run);
}
fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {

View File

@ -1,7 +1,6 @@
use crate::{
archetype::ArchetypeComponentId,
change_detection::MAX_CHANGE_AGE,
component::ComponentId,
component::{ComponentId, Tick},
query::Access,
system::{
check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
@ -95,7 +94,7 @@ where
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
let saved_last_tick = world.last_change_tick;
world.last_change_tick = self.system_meta.last_change_tick;
world.last_change_tick = self.system_meta.last_run;
let params = F::Param::get_param(
self.param_state.as_mut().expect(PARAM_MESSAGE),
@ -104,7 +103,7 @@ where
let out = self.func.run(world, input, params);
let change_tick = world.change_tick.get_mut();
self.system_meta.last_change_tick = *change_tick;
self.system_meta.last_run.set(*change_tick);
*change_tick = change_tick.wrapping_add(1);
world.last_change_tick = saved_last_tick;
@ -116,12 +115,12 @@ where
true
}
fn get_last_change_tick(&self) -> u32 {
self.system_meta.last_change_tick
fn get_last_run(&self) -> Tick {
self.system_meta.last_run
}
fn set_last_change_tick(&mut self, last_change_tick: u32) {
self.system_meta.last_change_tick = last_change_tick;
fn set_last_run(&mut self, last_run: Tick) {
self.system_meta.last_run = last_run;
}
#[inline]
@ -134,16 +133,16 @@ where
#[inline]
fn initialize(&mut self, world: &mut World) {
self.world_id = Some(world.id());
self.system_meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
self.param_state = Some(F::Param::init(world, &mut self.system_meta));
}
fn update_archetype_component_access(&mut self, _world: &World) {}
#[inline]
fn check_change_tick(&mut self, change_tick: u32) {
fn check_change_tick(&mut self, change_tick: Tick) {
check_system_change_tick(
&mut self.system_meta.last_change_tick,
&mut self.system_meta.last_run,
change_tick,
self.system_meta.name.as_ref(),
);

View File

@ -1,7 +1,6 @@
use crate::{
archetype::{ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
change_detection::MAX_CHANGE_AGE,
component::ComponentId,
component::{ComponentId, Tick},
prelude::FromWorld,
query::{Access, FilteredAccessSet},
system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
@ -22,7 +21,7 @@ pub struct SystemMeta {
// NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
// SystemParams from overriding each other
is_send: bool,
pub(crate) last_change_tick: u32,
pub(crate) last_run: Tick,
}
impl SystemMeta {
@ -32,7 +31,7 @@ impl SystemMeta {
archetype_component_access: Access::default(),
component_access_set: FilteredAccessSet::default(),
is_send: true,
last_change_tick: 0,
last_run: Tick::new(0),
}
}
@ -151,7 +150,7 @@ pub struct SystemState<Param: SystemParam + 'static> {
impl<Param: SystemParam> SystemState<Param> {
pub fn new(world: &mut World) -> Self {
let mut meta = SystemMeta::new::<Param>();
meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
meta.last_run = world.change_tick().relative_to(Tick::MAX);
let param_state = Param::init_state(world, &mut meta);
Self {
meta,
@ -288,10 +287,10 @@ impl<Param: SystemParam> SystemState<Param> {
unsafe fn fetch<'w, 's>(
&'s mut self,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> SystemParamItem<'w, 's, Param> {
let param = Param::get_param(&mut self.param_state, &self.meta, world, change_tick);
self.meta.last_change_tick = change_tick;
self.meta.last_run = change_tick;
param
}
}
@ -464,16 +463,16 @@ where
change_tick,
);
let out = self.func.run(input, params);
self.system_meta.last_change_tick = change_tick;
self.system_meta.last_run = change_tick;
out
}
fn get_last_change_tick(&self) -> u32 {
self.system_meta.last_change_tick
fn get_last_run(&self) -> Tick {
self.system_meta.last_run
}
fn set_last_change_tick(&mut self, last_change_tick: u32) {
self.system_meta.last_change_tick = last_change_tick;
fn set_last_run(&mut self, last_run: Tick) {
self.system_meta.last_run = last_run;
}
#[inline]
@ -485,7 +484,7 @@ where
#[inline]
fn initialize(&mut self, world: &mut World) {
self.world_id = Some(world.id());
self.system_meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE);
self.system_meta.last_run = world.change_tick().relative_to(Tick::MAX);
self.param_state = Some(F::Param::init_state(world, &mut self.system_meta));
}
@ -507,9 +506,9 @@ where
}
#[inline]
fn check_change_tick(&mut self, change_tick: u32) {
fn check_change_tick(&mut self, change_tick: Tick) {
check_system_change_tick(
&mut self.system_meta.last_change_tick,
&mut self.system_meta.last_run,
change_tick,
self.system_meta.name.as_ref(),
);

View File

@ -157,7 +157,7 @@ mod tests {
archetype::{ArchetypeComponentId, Archetypes},
bundle::Bundles,
change_detection::DetectChanges,
component::{Component, Components},
component::{Component, Components, Tick},
entity::{Entities, Entity},
prelude::AnyOf,
query::{Added, Changed, Or, With, Without},
@ -1227,7 +1227,7 @@ mod tests {
let world2 = World::new();
let qstate = world1.query::<()>();
// SAFETY: doesnt access anything
let query = unsafe { Query::new(&world2, &qstate, 0, 0, false) };
let query = unsafe { Query::new(&world2, &qstate, Tick::new(0), Tick::new(0), false) };
query.iter();
}

View File

@ -1,5 +1,5 @@
use crate::{
component::Component,
component::{Component, Tick},
entity::Entity,
query::{
BatchingStrategy, QueryCombinationIter, QueryEntityError, QueryIter, QueryManyIter,
@ -276,8 +276,8 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug};
pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
world: &'world World,
state: &'state QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
// SAFETY: This is used to ensure that `get_component_mut::<C>` properly fails when a Query writes C
// and gets converted to a read-only query using `to_readonly`. Without checking this, `get_component_mut` relies on
// QueryState's archetype_component_access, which will continue allowing write access to C after being cast to
@ -288,7 +288,7 @@ pub struct Query<'world, 'state, Q: WorldQuery, F: ReadOnlyWorldQuery = ()> {
impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> std::fmt::Debug for Query<'w, 's, Q, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Query {{ matched entities: {}, world: {:?}, state: {:?}, last_change_tick: {}, change_tick: {} }}", self.iter().count(), self.world, self.state, self.last_change_tick, self.change_tick)
write!(f, "Query {{ matched entities: {}, world: {:?}, state: {:?}, last_run: {:?}, this_run: {:?} }}", self.iter().count(), self.world, self.state, self.last_run, self.this_run)
}
}
@ -307,8 +307,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
pub(crate) unsafe fn new(
world: &'w World,
state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
force_read_only_component_access: bool,
) -> Self {
state.validate_world(world);
@ -317,8 +317,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
force_read_only_component_access,
world,
state,
last_change_tick,
change_tick,
last_run,
this_run,
}
}
@ -334,8 +334,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
Query::new(
self.world,
new_state,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
// SAFETY: this must be set to true or `get_component_mut` will be unsound. See the comments
// on this field for more details
true,
@ -372,11 +372,9 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state.as_readonly().iter_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.as_readonly()
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
@ -410,7 +408,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
@ -442,8 +440,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
unsafe {
self.state.as_readonly().iter_combinations_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -474,11 +472,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state.iter_combinations_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
@ -532,8 +527,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
self.state.as_readonly().iter_many_unchecked_manual(
entities,
self.world,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -585,8 +580,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
self.state.iter_many_unchecked_manual(
entities,
self.world,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -606,7 +601,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SEMI-SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
}
/// Iterates over all possible combinations of `K` query items without repetition.
@ -625,11 +620,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
) -> QueryCombinationIter<'_, 's, Q, F, K> {
// SEMI-SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state.iter_combinations_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run)
}
/// Returns an [`Iterator`] over the query items generated from an [`Entity`] list.
@ -650,12 +642,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
where
EntityList::Item: Borrow<Entity>,
{
self.state.iter_many_unchecked_manual(
entities,
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.iter_many_unchecked_manual(entities, self.world, self.last_run, self.this_run)
}
/// Runs `f` on each read-only query item.
@ -690,8 +678,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
self.state.as_readonly().for_each_unchecked_manual(
self.world,
f,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
);
};
}
@ -725,12 +713,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SAFETY: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict
unsafe {
self.state.for_each_unchecked_manual(
self.world,
f,
self.last_change_tick,
self.change_tick,
);
self.state
.for_each_unchecked_manual(self.world, f, self.last_run, self.this_run);
};
}
@ -801,8 +785,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
self.state.as_readonly().get_unchecked_manual(
self.world,
entity,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -823,12 +807,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
) -> Result<[ROQueryItem<'_, Q>; N], QueryEntityError> {
// SAFETY: it is the scheduler's responsibility to ensure that `Query` is never handed out on the wrong `World`.
unsafe {
self.state.get_many_read_only_manual(
self.world,
entities,
self.last_change_tick,
self.change_tick,
)
self.state
.get_many_read_only_manual(self.world, entities, self.last_run, self.this_run)
}
}
@ -910,12 +890,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state.get_unchecked_manual(
self.world,
entity,
self.last_change_tick,
self.change_tick,
)
self.state
.get_unchecked_manual(self.world, entity, self.last_run, self.this_run)
}
}
@ -934,12 +910,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
) -> Result<[Q::Item<'_>; N], QueryEntityError> {
// SAFETY: scheduler ensures safe Query world access
unsafe {
self.state.get_many_unchecked_manual(
self.world,
entities,
self.last_change_tick,
self.change_tick,
)
self.state
.get_many_unchecked_manual(self.world, entities, self.last_run, self.this_run)
}
}
@ -1013,7 +985,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SEMI-SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state
.get_unchecked_manual(self.world, entity, self.last_change_tick, self.change_tick)
.get_unchecked_manual(self.world, entity, self.last_run, self.this_run)
}
/// Returns a shared reference to the component `T` of the given [`Entity`].
@ -1151,7 +1123,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
.has_write(archetype_component)
{
entity_ref
.get_mut_using_ticks::<T>(self.last_change_tick, self.change_tick)
.get_mut_using_ticks::<T>(self.last_run, self.this_run)
.ok_or(QueryComponentError::MissingComponent)
} else {
Err(QueryComponentError::MissingWriteAccess)
@ -1227,8 +1199,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
unsafe {
self.state.as_readonly().get_single_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -1296,11 +1268,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// the query ensures mutable access to the components it accesses, and the query
// is uniquely borrowed
unsafe {
self.state.get_single_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.get_single_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
@ -1327,7 +1296,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
#[inline]
pub fn is_empty(&self) -> bool {
self.state
.is_empty(self.world, self.last_change_tick, self.change_tick)
.is_empty(self.world, self.last_run, self.this_run)
}
/// Returns `true` if the given [`Entity`] matches the query.
@ -1358,7 +1327,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
unsafe {
self.state
.as_nop()
.get_unchecked_manual(self.world, entity, self.last_change_tick, self.change_tick)
.get_unchecked_manual(self.world, entity, self.last_run, self.this_run)
.is_ok()
}
}
@ -1459,8 +1428,8 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
self.state.as_readonly().get_unchecked_manual(
self.world,
entity,
self.last_change_tick,
self.change_tick,
self.last_run,
self.this_run,
)
}
}
@ -1493,11 +1462,9 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
// SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state.as_readonly().iter_unchecked_manual(
self.world,
self.last_change_tick,
self.change_tick,
)
self.state
.as_readonly()
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
}

View File

@ -1,10 +1,8 @@
use bevy_utils::tracing::warn;
use core::fmt::Debug;
use crate::{
archetype::ArchetypeComponentId, change_detection::MAX_CHANGE_AGE, component::ComponentId,
query::Access, world::World,
};
use crate::component::Tick;
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};
use std::any::TypeId;
use std::borrow::Cow;
@ -63,19 +61,20 @@ pub trait System: Send + Sync + 'static {
fn initialize(&mut self, _world: &mut World);
/// Update the system's archetype component [`Access`].
fn update_archetype_component_access(&mut self, world: &World);
fn check_change_tick(&mut self, change_tick: u32);
fn check_change_tick(&mut self, change_tick: Tick);
/// Returns the system's default [system sets](crate::schedule::SystemSet).
fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
Vec::new()
}
/// Gets the system's last change tick
fn get_last_change_tick(&self) -> u32;
/// Sets the system's last change tick
/// Gets the tick indicating the last time this system ran.
fn get_last_run(&self) -> Tick;
/// Overwrites the tick indicating the last time this system ran.
///
/// # Warning
/// This is a complex and error-prone operation, that can have unexpected consequences on any system relying on this code.
/// However, it can be an essential escape hatch when, for example,
/// you are trying to synchronize representations using change detection and need to avoid infinite recursion.
fn set_last_change_tick(&mut self, last_change_tick: u32);
fn set_last_run(&mut self, last_run: Tick);
}
/// [`System`] types that do not modify the [`World`] when run.
@ -91,23 +90,14 @@ pub unsafe trait ReadOnlySystem: System {}
/// A convenience type alias for a boxed [`System`] trait object.
pub type BoxedSystem<In = (), Out = ()> = Box<dyn System<In = In, Out = Out>>;
pub(crate) fn check_system_change_tick(
last_change_tick: &mut u32,
change_tick: u32,
system_name: &str,
) {
let age = change_tick.wrapping_sub(*last_change_tick);
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true
// so long as this check always runs before that can happen.
if age > MAX_CHANGE_AGE {
pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, system_name: &str) {
if last_run.check_tick(this_run) {
let age = this_run.relative_to(*last_run).get();
warn!(
"System '{}' has not run for {} ticks. \
"System '{system_name}' has not run for {age} ticks. \
Changes older than {} ticks will not be detected.",
system_name,
age,
MAX_CHANGE_AGE - 1,
Tick::MAX.get() - 1,
);
*last_change_tick = change_tick.wrapping_sub(MAX_CHANGE_AGE);
}
}

View File

@ -3,7 +3,7 @@ use crate::{
archetype::{Archetype, Archetypes},
bundle::Bundles,
change_detection::{Ticks, TicksMut},
component::{ComponentId, ComponentTicks, Components},
component::{ComponentId, ComponentTicks, Components, Tick},
entity::Entities,
query::{
Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery,
@ -169,7 +169,7 @@ pub unsafe trait SystemParam: Sized {
state: &'state mut Self::State,
system_meta: &SystemMeta,
world: &'world World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'world, 'state>;
}
@ -227,15 +227,9 @@ unsafe impl<Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> SystemPara
state: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
Query::new(
world,
state,
system_meta.last_change_tick,
change_tick,
false,
)
Query::new(world, state, system_meta.last_run, change_tick, false)
}
}
@ -371,7 +365,7 @@ pub struct ParamSet<'w, 's, T: SystemParam> {
param_states: &'s mut T::State,
world: &'w World,
system_meta: SystemMeta,
change_tick: u32,
change_tick: Tick,
}
impl_param_set!();
@ -445,7 +439,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
let (ptr, ticks) = world
.as_unsafe_world_cell_migration_internal()
@ -462,8 +456,8 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
ticks: Ticks {
added: ticks.added.deref(),
changed: ticks.changed.deref(),
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
},
}
}
@ -486,7 +480,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
world
.as_unsafe_world_cell_migration_internal()
@ -496,8 +490,8 @@ unsafe impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
ticks: Ticks {
added: ticks.added.deref(),
changed: ticks.changed.deref(),
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
},
})
}
@ -540,7 +534,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
let value = world
.as_unsafe_world_cell_migration_internal()
@ -557,8 +551,8 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
ticks: TicksMut {
added: value.ticks.added,
changed: value.ticks.changed,
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
},
}
}
@ -578,7 +572,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
world
.as_unsafe_world_cell_migration_internal()
@ -588,8 +582,8 @@ unsafe impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
ticks: TicksMut {
added: value.ticks.added,
changed: value.ticks.changed,
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
},
})
}
@ -631,7 +625,7 @@ unsafe impl SystemParam for &'_ World {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world
}
@ -752,7 +746,7 @@ unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> {
state: &'s mut Self::State,
_system_meta: &SystemMeta,
_world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
Local(state.get())
}
@ -927,7 +921,7 @@ unsafe impl<T: SystemBuffer> SystemParam for Deferred<'_, T> {
state: &'s mut Self::State,
_system_meta: &SystemMeta,
_world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
Deferred(state.get())
}
@ -948,8 +942,8 @@ unsafe impl<T: SystemBuffer> SystemParam for Deferred<'_, T> {
pub struct NonSend<'w, T: 'static> {
pub(crate) value: &'w T,
ticks: ComponentTicks,
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
// SAFETY: Only reads a single World non-send resource
@ -967,13 +961,12 @@ where
impl<'w, T: 'static> NonSend<'w, T> {
/// Returns `true` if the resource was added after the system last ran.
pub fn is_added(&self) -> bool {
self.ticks.is_added(self.last_change_tick, self.change_tick)
self.ticks.is_added(self.last_run, self.this_run)
}
/// Returns `true` if the resource was added or mutably dereferenced after the system last ran.
pub fn is_changed(&self) -> bool {
self.ticks
.is_changed(self.last_change_tick, self.change_tick)
self.ticks.is_changed(self.last_run, self.this_run)
}
}
@ -992,8 +985,8 @@ impl<'a, T> From<NonSendMut<'a, T>> for NonSend<'a, T> {
added: nsm.ticks.added.to_owned(),
changed: nsm.ticks.changed.to_owned(),
},
change_tick: nsm.ticks.change_tick,
last_change_tick: nsm.ticks.last_change_tick,
this_run: nsm.ticks.this_run,
last_run: nsm.ticks.last_run,
}
}
}
@ -1034,7 +1027,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
let (ptr, ticks) = world
.as_unsafe_world_cell_migration_internal()
@ -1050,8 +1043,8 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
NonSend {
value: ptr.deref(),
ticks: ticks.read(),
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
}
}
}
@ -1073,7 +1066,7 @@ unsafe impl<T: 'static> SystemParam for Option<NonSend<'_, T>> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
world
.as_unsafe_world_cell_migration_internal()
@ -1081,8 +1074,8 @@ unsafe impl<T: 'static> SystemParam for Option<NonSend<'_, T>> {
.map(|(ptr, ticks)| NonSend {
value: ptr.deref(),
ticks: ticks.read(),
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
})
}
}
@ -1126,7 +1119,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
let (ptr, ticks) = world
.as_unsafe_world_cell_migration_internal()
@ -1140,7 +1133,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
});
NonSendMut {
value: ptr.assert_unique().deref_mut(),
ticks: TicksMut::from_tick_cells(ticks, system_meta.last_change_tick, change_tick),
ticks: TicksMut::from_tick_cells(ticks, system_meta.last_run, change_tick),
}
}
}
@ -1159,14 +1152,14 @@ unsafe impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> {
&mut component_id: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
world
.as_unsafe_world_cell_migration_internal()
.get_non_send_with_ticks(component_id)
.map(|(ptr, ticks)| NonSendMut {
value: ptr.assert_unique().deref_mut(),
ticks: TicksMut::from_tick_cells(ticks, system_meta.last_change_tick, change_tick),
ticks: TicksMut::from_tick_cells(ticks, system_meta.last_run, change_tick),
})
}
}
@ -1186,7 +1179,7 @@ unsafe impl<'a> SystemParam for &'a Archetypes {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world.archetypes()
}
@ -1207,7 +1200,7 @@ unsafe impl<'a> SystemParam for &'a Components {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world.components()
}
@ -1228,7 +1221,7 @@ unsafe impl<'a> SystemParam for &'a Entities {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world.entities()
}
@ -1249,7 +1242,7 @@ unsafe impl<'a> SystemParam for &'a Bundles {
_state: &'s mut Self::State,
_system_meta: &SystemMeta,
world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
world.bundles()
}
@ -1258,29 +1251,29 @@ unsafe impl<'a> SystemParam for &'a Bundles {
/// A [`SystemParam`] that reads the previous and current change ticks of the system.
///
/// A system's change ticks are updated each time it runs:
/// - `last_change_tick` copies the previous value of `change_tick`
/// - `change_tick` copies the current value of [`World::read_change_tick`]
/// - `last_run` copies the previous value of `change_tick`
/// - `this_run` copies the current value of [`World::read_change_tick`]
///
/// Component change ticks that are more recent than `last_change_tick` will be detected by the system.
/// Component change ticks that are more recent than `last_run` will be detected by the system.
/// Those can be read by calling [`last_changed`](crate::change_detection::DetectChanges::last_changed)
/// on a [`Mut<T>`](crate::change_detection::Mut) or [`ResMut<T>`](crate::change_detection::ResMut).
#[derive(Debug)]
pub struct SystemChangeTick {
last_change_tick: u32,
change_tick: u32,
last_run: Tick,
this_run: Tick,
}
impl SystemChangeTick {
/// Returns the current [`World`] change tick seen by the system.
#[inline]
pub fn change_tick(&self) -> u32 {
self.change_tick
pub fn this_run(&self) -> Tick {
self.this_run
}
/// Returns the [`World`] change tick seen by the system the previous time it ran.
#[inline]
pub fn last_change_tick(&self) -> u32 {
self.last_change_tick
pub fn last_run(&self) -> Tick {
self.last_run
}
}
@ -1298,11 +1291,11 @@ unsafe impl SystemParam for SystemChangeTick {
_state: &'s mut Self::State,
system_meta: &SystemMeta,
_world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
SystemChangeTick {
last_change_tick: system_meta.last_change_tick,
change_tick,
last_run: system_meta.last_run,
this_run: change_tick,
}
}
}
@ -1368,7 +1361,7 @@ unsafe impl SystemParam for SystemName<'_> {
name: &'s mut Self::State,
_system_meta: &SystemMeta,
_world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
SystemName { name }
}
@ -1410,7 +1403,7 @@ macro_rules! impl_system_param_tuple {
state: &'s mut Self::State,
_system_meta: &SystemMeta,
_world: &'w World,
_change_tick: u32,
_change_tick: Tick,
) -> Self::Item<'w, 's> {
let ($($param,)*) = state;
@ -1534,7 +1527,7 @@ unsafe impl<P: SystemParam + 'static> SystemParam for StaticSystemParam<'_, '_,
state: &'state mut Self::State,
system_meta: &SystemMeta,
world: &'world World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'world, 'state> {
// SAFETY: Defer to the safety of P::SystemParam
StaticSystemParam(P::get_param(state, system_meta, world, change_tick))

View File

@ -1,4 +1,5 @@
use crate::{
component::Tick,
storage::SparseSetIndex,
system::{ReadOnlySystemParam, SystemParam},
world::{FromWorld, World},
@ -56,7 +57,7 @@ unsafe impl SystemParam for WorldId {
_: &'state mut Self::State,
_: &crate::system::SystemMeta,
world: &'world super::World,
_: u32,
_: Tick,
) -> Self::Item<'world, 'state> {
world.id
}

View File

@ -12,7 +12,7 @@ use crate::{
archetype::{ArchetypeComponentId, ArchetypeId, ArchetypeRow, Archetypes},
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::{MutUntyped, TicksMut},
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components},
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, Tick},
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
event::{Event, Events},
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
@ -64,8 +64,8 @@ pub struct World {
/// Access cache used by [WorldCell]. Is only accessed in the `Drop` impl of `WorldCell`.
pub(crate) archetype_component_access: ArchetypeComponentAccess,
pub(crate) change_tick: AtomicU32,
pub(crate) last_change_tick: u32,
pub(crate) last_check_tick: u32,
pub(crate) last_change_tick: Tick,
pub(crate) last_check_tick: Tick,
}
impl Default for World {
@ -82,8 +82,8 @@ impl Default for World {
// Default value is `1`, and `last_change_tick`s default to `0`, such that changes
// are detected on first system runs and for direct world queries.
change_tick: AtomicU32::new(1),
last_change_tick: 0,
last_check_tick: 0,
last_change_tick: Tick::new(0),
last_check_tick: Tick::new(0),
}
}
}
@ -493,6 +493,7 @@ impl World {
/// ```
pub fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityMut {
self.flush();
let change_tick = self.change_tick();
let entity = self.entities.alloc();
let entity_location = {
let bundle_info = self
@ -503,7 +504,7 @@ impl World {
&mut self.archetypes,
&mut self.components,
&mut self.storages,
*self.change_tick.get_mut(),
change_tick,
);
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
@ -1174,8 +1175,7 @@ impl World {
{
self.flush();
let iter = iter.into_iter();
let change_tick = *self.change_tick.get_mut();
let change_tick = self.change_tick();
let bundle_info = self
.bundles
@ -1306,8 +1306,8 @@ impl World {
ticks: TicksMut {
added: &mut ticks.added,
changed: &mut ticks.changed,
last_change_tick,
change_tick,
last_run: last_change_tick,
this_run: change_tick,
},
};
let result = f(self, value_mut);
@ -1467,9 +1467,11 @@ impl World {
}
}
/// Increments the world's current change tick, and returns the old value.
#[inline]
pub fn increment_change_tick(&self) -> u32 {
self.change_tick.fetch_add(1, Ordering::AcqRel)
pub fn increment_change_tick(&self) -> Tick {
let prev_tick = self.change_tick.fetch_add(1, Ordering::AcqRel);
Tick::new(prev_tick)
}
/// Reads the current change tick of this world.
@ -1477,8 +1479,9 @@ impl World {
/// If you have exclusive (`&mut`) access to the world, consider using [`change_tick()`](Self::change_tick),
/// which is more efficient since it does not require atomic synchronization.
#[inline]
pub fn read_change_tick(&self) -> u32 {
self.change_tick.load(Ordering::Acquire)
pub fn read_change_tick(&self) -> Tick {
let tick = self.change_tick.load(Ordering::Acquire);
Tick::new(tick)
}
/// Reads the current change tick of this world.
@ -1486,12 +1489,13 @@ impl World {
/// This does the same thing as [`read_change_tick()`](Self::read_change_tick), only this method
/// is more efficient since it does not require atomic synchronization.
#[inline]
pub fn change_tick(&mut self) -> u32 {
*self.change_tick.get_mut()
pub fn change_tick(&mut self) -> Tick {
let tick = *self.change_tick.get_mut();
Tick::new(tick)
}
#[inline]
pub fn last_change_tick(&self) -> u32 {
pub fn last_change_tick(&self) -> Tick {
self.last_change_tick
}
@ -1503,7 +1507,7 @@ impl World {
// TODO: benchmark and optimize
pub fn check_change_ticks(&mut self) {
let change_tick = self.change_tick();
if change_tick.wrapping_sub(self.last_check_tick) < CHECK_TICK_THRESHOLD {
if change_tick.relative_to(self.last_check_tick).get() < CHECK_TICK_THRESHOLD {
return;
}

View File

@ -25,6 +25,8 @@ where
// necessary
world.flush();
let change_tick = world.change_tick();
let (lower, upper) = iter.size_hint();
let length = upper.unwrap_or(lower);
@ -37,7 +39,7 @@ where
&mut world.archetypes,
&mut world.components,
&mut world.storages,
*world.change_tick.get_mut(),
change_tick,
);
spawner.reserve_storage(length);

View File

@ -6,7 +6,7 @@ use crate::{
bundle::Bundles,
change_detection::{MutUntyped, TicksMut},
component::{
ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, TickCells,
ComponentId, ComponentStorage, ComponentTicks, Components, StorageType, Tick, TickCells,
},
entity::{Entities, Entity, EntityLocation},
prelude::Component,
@ -185,16 +185,17 @@ impl<'w> UnsafeWorldCell<'w> {
/// Reads the current change tick of this world.
#[inline]
pub fn read_change_tick(self) -> u32 {
pub fn read_change_tick(self) -> Tick {
// SAFETY:
// - we only access world metadata
unsafe { self.world_metadata() }
let tick = unsafe { self.world_metadata() }
.change_tick
.load(Ordering::Acquire)
.load(Ordering::Acquire);
Tick::new(tick)
}
#[inline]
pub fn last_change_tick(self) -> u32 {
pub fn last_change_tick(self) -> Tick {
// SAFETY:
// - we only access world metadata
unsafe { self.world_metadata() }.last_change_tick
@ -655,8 +656,8 @@ impl<'w> UnsafeEntityCell<'w> {
#[inline]
pub(crate) unsafe fn get_mut_using_ticks<T: Component>(
&self,
last_change_tick: u32,
change_tick: u32,
last_change_tick: Tick,
change_tick: Tick,
) -> Option<Mut<'w, T>> {
let component_id = self.world.components().get_id(TypeId::of::<T>())?;

View File

@ -1,5 +1,6 @@
use crate::MainWorld;
use bevy_ecs::{
component::Tick,
prelude::*,
system::{ReadOnlySystemParam, SystemMeta, SystemParam, SystemParamItem, SystemState},
};
@ -76,7 +77,7 @@ where
state: &'s mut Self::State,
system_meta: &SystemMeta,
world: &'w World,
change_tick: u32,
change_tick: Tick,
) -> Self::Item<'w, 's> {
// SAFETY:
// - The caller ensures that `world` is the same one that `init_state` was called with.