diff --git a/crates/bevy_ecs/macros/src/fetch.rs b/crates/bevy_ecs/macros/src/fetch.rs index 0308d8dc2338b..8bb5597f0d292 100644 --- a/crates/bevy_ecs/macros/src/fetch.rs +++ b/crates/bevy_ecs/macros/src/fetch.rs @@ -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, ) -> ::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(),)* diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b4dde955dd388..dbafe0d9e3ead 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -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 diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index ff4ceb03af2f7..66fe6cdbcfb8b 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -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> { diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index e9d52e828108d..273b9b119d8ef 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -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> 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::>(); 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 = 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 = 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] diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index ade0eb6b7d0a8..a7c090e0f4e42 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -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] + 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_change_tick`. + /// 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`](crate::change_detection::Mut), [`ResMut`](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; } } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 9ff765648fea0..23d52c795db34 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -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 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 WorldQuery for Option { 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 ReadOnlyWorldQuery for Option {} #[deprecated = "`ChangeTrackers` will be removed in bevy 0.11. Use `bevy_ecs::prelude::Ref` instead."] pub struct ChangeTrackers { 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, } @@ -1134,8 +1130,8 @@ impl std::fmt::Debug for ChangeTrackers { 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 std::fmt::Debug for ChangeTrackers { impl ChangeTrackers { /// 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, - last_change_tick: u32, - change_tick: u32, + last_run: Tick, + this_run: Tick, } #[allow(deprecated)] @@ -1192,8 +1187,8 @@ unsafe impl WorldQuery for ChangeTrackers { 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 WorldQuery for ChangeTrackers { .debug_checked_unwrap() }), marker: PhantomData, - last_change_tick, - change_tick, + last_run, + this_run, } } @@ -1217,8 +1212,8 @@ unsafe impl WorldQuery for ChangeTrackers { 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 WorldQuery for ChangeTrackers { } }, 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 WorldQuery for ChangeTrackers { .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 WorldQuery for NopWorldQuery { 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> {} diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index b2dd9023a69a8..c0b91b54afd10 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -50,13 +50,7 @@ unsafe impl WorldQuery for With { 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 WorldQuery for Without { 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>>, marker: PhantomData, 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) } } } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index d456b044106f4..893df47aac3a3 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -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, - 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, 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, - 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, - 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, - 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, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index f4ae1a6837d62..d85c2965dec27 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -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 QueryState { /// 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 QueryState { &self, world: &'w World, entity: Entity, - last_change_tick: u32, - change_tick: u32, + last_run: Tick, + this_run: Tick, ) -> Result, QueryEntityError> { let location = world .entities @@ -407,8 +407,8 @@ impl QueryState { .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 QueryState { &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 QueryState { &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 QueryState { 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 QueryState { 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 QueryState { &'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, { - 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 QueryState { 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 QueryState { &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 QueryState { 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 QueryState { 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 QueryState { 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 QueryState { 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, 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(); diff --git a/crates/bevy_ecs/src/removal_detection.rs b/crates/bevy_ecs/src/removal_detection.rs index 004f08ca347e7..139c092bb8b50 100644 --- a/crates/bevy_ecs/src/removal_detection.rs +++ b/crates/bevy_ecs/src/removal_detection.rs @@ -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() } diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index df4b26c6cb6ad..d466e8aef1e0e 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -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); } diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index bdebad152e716..f856bf9bb791d 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -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 ResourceData { /// # 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> { + pub(crate) fn get_mut(&mut self, last_run: Tick, this_run: Tick) -> Option> { 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 ResourceData { /// # 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 Resources { }) } - 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); } diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index 217609dc8c5ac..d15aca7c4ae84 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -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); } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index d456d1f900b56..bae50e86c22a4 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -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); } diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 51304831d21ce..ec782e715bd19 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -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> { diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 1fde9619e2436..d82c6ff153bee 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -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(), ); diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index fe6701df605c2..0e88ad0187af4 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -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 { impl SystemState { pub fn new(world: &mut World) -> Self { let mut meta = SystemMeta::new::(); - 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 SystemState { 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(), ); diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 0e88adf24e81d..61351523bc2ea 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -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(); } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 0c093b2587516..e8c54472dae42 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -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, - last_change_tick: u32, - change_tick: u32, + last_run: Tick, + this_run: Tick, // SAFETY: This is used to ensure that `get_component_mut::` 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, - 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, { - 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::(self.last_change_tick, self.change_tick) + .get_mut_using_ticks::(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) } } } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 78980e9063b81..307dd2bc7da61 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -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> { 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 = Box>; -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); } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 437a726f4413d..018c79630f455 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -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 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> { &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> { 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> { &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> { 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 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 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> 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 SystemParam for Option> { &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 SystemParam for Option> { .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> { &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`](crate::change_detection::Mut) or [`ResMut`](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 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)) diff --git a/crates/bevy_ecs/src/world/identifier.rs b/crates/bevy_ecs/src/world/identifier.rs index e6a78be5ca2f4..55e2b6a5fa350 100644 --- a/crates/bevy_ecs/src/world/identifier.rs +++ b/crates/bevy_ecs/src/world/identifier.rs @@ -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 } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 0f78884e16944..0832141f4bf02 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -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(&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; } diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index f5e1bd2792e2b..965ff9754021c 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -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); diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 364cef1bc3371..99231fe3f32ef 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -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( &self, - last_change_tick: u32, - change_tick: u32, + last_change_tick: Tick, + change_tick: Tick, ) -> Option> { let component_id = self.world.components().get_id(TypeId::of::())?; diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 92290407f5b25..898dfe0e28ae0 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -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.