diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component/mod.rs index 74a0118c25..e643894196 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component/mod.rs @@ -2,14 +2,16 @@ mod clone; mod queued_registration; +mod tick; pub use clone::*; pub use queued_registration::*; +pub use tick::*; use crate::{ archetype::ArchetypeFlags, bundle::BundleInfo, - change_detection::{MaybeLocation, MAX_CHANGE_AGE}, + change_detection::MaybeLocation, entity::{Entity, EntityMapper}, lifecycle::{ComponentHook, ComponentHooks}, query::DebugCheckedUnwrap, @@ -20,20 +22,18 @@ use crate::{ }; use alloc::{borrow::Cow, format, vec::Vec}; pub use bevy_ecs_macros::Component; -use bevy_ecs_macros::Event; use bevy_platform::sync::Arc; use bevy_platform::{ collections::{HashMap, HashSet}, sync::PoisonError, }; -use bevy_ptr::{OwningPtr, UnsafeCellDeref}; +use bevy_ptr::OwningPtr; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; use bevy_utils::{prelude::DebugName, TypeIdMap}; use core::{ alloc::Layout, any::{Any, TypeId}, - cell::UnsafeCell, fmt::Debug, marker::PhantomData, mem::needs_drop, @@ -2020,198 +2020,6 @@ impl Components { } } -/// A value that tracks when a system ran relative to other systems. -/// This is used to power change detection. -/// -/// *Note* that a system that hasn't been run yet has a `Tick` of 0. -#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)] -#[cfg_attr( - feature = "bevy_reflect", - derive(Reflect), - reflect(Debug, Hash, PartialEq, Clone) -)] -pub struct Tick { - tick: u32, -} - -impl Tick { - /// The maximum relative age for a change tick. - /// The value of this is equal to [`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); - - /// Creates a new [`Tick`] wrapping the given value. - #[inline] - 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; - } - - /// Returns `true` if this `Tick` occurred since the system's `last_run`. - /// - /// `this_run` is the current tick of the system, used as a reference to help deal with wraparound. - #[inline] - 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 = 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 - } - - /// Returns a change tick representing the relationship between `self` and `other`. - #[inline] - pub(crate) fn relative_to(self, other: Self) -> Self { - let tick = self.tick.wrapping_sub(other.tick); - Self { tick } - } - - /// Wraps this change tick's value if it exceeds [`Tick::MAX`]. - /// - /// Returns `true` if wrapping was performed. Otherwise, returns `false`. - #[inline] - pub fn check_tick(&mut self, check: CheckChangeTicks) -> bool { - let age = check.present_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 = check.present_tick().relative_to(Self::MAX); - true - } else { - false - } - } -} - -/// An observer [`Event`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make -/// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus -/// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old. -/// -/// # Example -/// -/// Here a schedule is stored in a custom resource. This way the systems in it would not have their change -/// ticks automatically updated via [`World::check_change_ticks`], possibly causing `Tick`-related bugs on -/// long-running apps. -/// -/// To fix that, add an observer for this event that calls the schedule's -/// [`Schedule::check_change_ticks`](bevy_ecs::schedule::Schedule::check_change_ticks). -/// -/// ``` -/// use bevy_ecs::prelude::*; -/// use bevy_ecs::component::CheckChangeTicks; -/// -/// #[derive(Resource)] -/// struct CustomSchedule(Schedule); -/// -/// # let mut world = World::new(); -/// world.add_observer(|check: On, mut schedule: ResMut| { -/// schedule.0.check_change_ticks(*check); -/// }); -/// ``` -#[derive(Debug, Clone, Copy, Event)] -pub struct CheckChangeTicks(pub(crate) Tick); - -impl CheckChangeTicks { - /// Get the present `Tick` that other ticks get compared to. - pub fn present_tick(self) -> Tick { - self.0 - } -} - -/// Interior-mutable access to the [`Tick`]s for a single component or resource. -#[derive(Copy, Clone, Debug)] -pub struct TickCells<'a> { - /// The tick indicating when the value was added to the world. - pub added: &'a UnsafeCell, - /// The tick indicating the last time the value was modified. - pub changed: &'a UnsafeCell, -} - -impl<'a> TickCells<'a> { - /// # Safety - /// All cells contained within must uphold the safety invariants of [`UnsafeCellDeref::read`]. - #[inline] - pub(crate) unsafe fn read(&self) -> ComponentTicks { - ComponentTicks { - // SAFETY: The callers uphold the invariants for `read`. - added: unsafe { self.added.read() }, - // SAFETY: The callers uphold the invariants for `read`. - changed: unsafe { self.changed.read() }, - } - } -} - -/// Records when a component or resource was added and when it was last mutably dereferenced (or added). -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))] -pub struct ComponentTicks { - /// Tick recording the time this component or resource was added. - pub added: Tick, - - /// Tick recording the time this component or resource was most recently changed. - pub changed: Tick, -} - -impl ComponentTicks { - /// Returns `true` if the component or resource was added after the system last ran - /// (or the system is running for the first time). - #[inline] - pub fn is_added(&self, last_run: Tick, this_run: Tick) -> bool { - self.added.is_newer_than(last_run, this_run) - } - - /// Returns `true` if the component or resource was added or mutably dereferenced after the system last ran - /// (or the system is running for the first time). - #[inline] - pub fn is_changed(&self, last_run: Tick, this_run: Tick) -> bool { - self.changed.is_newer_than(last_run, this_run) - } - - /// Creates a new instance with the same change tick for `added` and `changed`. - pub fn new(change_tick: Tick) -> Self { - Self { - added: change_tick, - changed: change_tick, - } - } - - /// Manually sets the change tick. - /// - /// This is normally done automatically via the [`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 - /// ```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: Tick) { - self.changed = change_tick; - } -} - /// A [`SystemParam`] that provides access to the [`ComponentId`] for a specific component type. /// /// # Example diff --git a/crates/bevy_ecs/src/component/tick.rs b/crates/bevy_ecs/src/component/tick.rs new file mode 100644 index 0000000000..2417832c9a --- /dev/null +++ b/crates/bevy_ecs/src/component/tick.rs @@ -0,0 +1,199 @@ +use bevy_ecs_macros::Event; +use bevy_ptr::UnsafeCellDeref; +#[cfg(feature = "bevy_reflect")] +use bevy_reflect::Reflect; +use core::cell::UnsafeCell; + +use crate::change_detection::MAX_CHANGE_AGE; + +/// A value that tracks when a system ran relative to other systems. +/// This is used to power change detection. +/// +/// *Note* that a system that hasn't been run yet has a `Tick` of 0. +#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)] +#[cfg_attr( + feature = "bevy_reflect", + derive(Reflect), + reflect(Debug, Hash, PartialEq, Clone) +)] +pub struct Tick { + tick: u32, +} + +impl Tick { + /// The maximum relative age for a change tick. + /// The value of this is equal to [`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); + + /// Creates a new [`Tick`] wrapping the given value. + #[inline] + 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; + } + + /// Returns `true` if this `Tick` occurred since the system's `last_run`. + /// + /// `this_run` is the current tick of the system, used as a reference to help deal with wraparound. + #[inline] + 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 = 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 + } + + /// Returns a change tick representing the relationship between `self` and `other`. + #[inline] + pub(crate) fn relative_to(self, other: Self) -> Self { + let tick = self.tick.wrapping_sub(other.tick); + Self { tick } + } + + /// Wraps this change tick's value if it exceeds [`Tick::MAX`]. + /// + /// Returns `true` if wrapping was performed. Otherwise, returns `false`. + #[inline] + pub fn check_tick(&mut self, check: CheckChangeTicks) -> bool { + let age = check.present_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 = check.present_tick().relative_to(Self::MAX); + true + } else { + false + } + } +} + +/// An observer [`Event`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make +/// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus +/// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old. +/// +/// # Example +/// +/// Here a schedule is stored in a custom resource. This way the systems in it would not have their change +/// ticks automatically updated via [`World::check_change_ticks`], possibly causing `Tick`-related bugs on +/// long-running apps. +/// +/// To fix that, add an observer for this event that calls the schedule's +/// [`Schedule::check_change_ticks`](bevy_ecs::schedule::Schedule::check_change_ticks). +/// +/// ``` +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::component::CheckChangeTicks; +/// +/// #[derive(Resource)] +/// struct CustomSchedule(Schedule); +/// +/// # let mut world = World::new(); +/// world.add_observer(|check: On, mut schedule: ResMut| { +/// schedule.0.check_change_ticks(*check); +/// }); +/// ``` +#[derive(Debug, Clone, Copy, Event)] +pub struct CheckChangeTicks(pub(crate) Tick); + +impl CheckChangeTicks { + /// Get the present `Tick` that other ticks get compared to. + pub fn present_tick(self) -> Tick { + self.0 + } +} + +/// Interior-mutable access to the [`Tick`]s for a single component or resource. +#[derive(Copy, Clone, Debug)] +pub struct TickCells<'a> { + /// The tick indicating when the value was added to the world. + pub added: &'a UnsafeCell, + /// The tick indicating the last time the value was modified. + pub changed: &'a UnsafeCell, +} + +impl<'a> TickCells<'a> { + /// # Safety + /// All cells contained within must uphold the safety invariants of [`UnsafeCellDeref::read`]. + #[inline] + pub(crate) unsafe fn read(&self) -> ComponentTicks { + ComponentTicks { + // SAFETY: The callers uphold the invariants for `read`. + added: unsafe { self.added.read() }, + // SAFETY: The callers uphold the invariants for `read`. + changed: unsafe { self.changed.read() }, + } + } +} + +/// Records when a component or resource was added and when it was last mutably dereferenced (or added). +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))] +pub struct ComponentTicks { + /// Tick recording the time this component or resource was added. + pub added: Tick, + + /// Tick recording the time this component or resource was most recently changed. + pub changed: Tick, +} + +impl ComponentTicks { + /// Returns `true` if the component or resource was added after the system last ran + /// (or the system is running for the first time). + #[inline] + pub fn is_added(&self, last_run: Tick, this_run: Tick) -> bool { + self.added.is_newer_than(last_run, this_run) + } + + /// Returns `true` if the component or resource was added or mutably dereferenced after the system last ran + /// (or the system is running for the first time). + #[inline] + pub fn is_changed(&self, last_run: Tick, this_run: Tick) -> bool { + self.changed.is_newer_than(last_run, this_run) + } + + /// Creates a new instance with the same change tick for `added` and `changed`. + pub fn new(change_tick: Tick) -> Self { + Self { + added: change_tick, + changed: change_tick, + } + } + + /// Manually sets the change tick. + /// + /// This is normally done automatically via the [`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 + /// ```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: Tick) { + self.changed = change_tick; + } +}