Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/component/tick.rs
6600 views
1
use bevy_ecs_macros::Event;
2
use bevy_ptr::UnsafeCellDeref;
3
#[cfg(feature = "bevy_reflect")]
4
use bevy_reflect::Reflect;
5
use core::cell::UnsafeCell;
6
7
use crate::change_detection::MAX_CHANGE_AGE;
8
9
/// A value that tracks when a system ran relative to other systems.
10
/// This is used to power change detection.
11
///
12
/// *Note* that a system that hasn't been run yet has a `Tick` of 0.
13
#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
14
#[cfg_attr(
15
feature = "bevy_reflect",
16
derive(Reflect),
17
reflect(Debug, Hash, PartialEq, Clone)
18
)]
19
pub struct Tick {
20
tick: u32,
21
}
22
23
impl Tick {
24
/// The maximum relative age for a change tick.
25
/// The value of this is equal to [`MAX_CHANGE_AGE`].
26
///
27
/// Since change detection will not work for any ticks older than this,
28
/// ticks are periodically scanned to ensure their relative values are below this.
29
pub const MAX: Self = Self::new(MAX_CHANGE_AGE);
30
31
/// Creates a new [`Tick`] wrapping the given value.
32
#[inline]
33
pub const fn new(tick: u32) -> Self {
34
Self { tick }
35
}
36
37
/// Gets the value of this change tick.
38
#[inline]
39
pub const fn get(self) -> u32 {
40
self.tick
41
}
42
43
/// Sets the value of this change tick.
44
#[inline]
45
pub fn set(&mut self, tick: u32) {
46
self.tick = tick;
47
}
48
49
/// Returns `true` if this `Tick` occurred since the system's `last_run`.
50
///
51
/// `this_run` is the current tick of the system, used as a reference to help deal with wraparound.
52
#[inline]
53
pub fn is_newer_than(self, last_run: Tick, this_run: Tick) -> bool {
54
// This works even with wraparound because the world tick (`this_run`) is always "newer" than
55
// `last_run` and `self.tick`, and we scan periodically to clamp `ComponentTicks` values
56
// so they never get older than `u32::MAX` (the difference would overflow).
57
//
58
// The clamp here ensures determinism (since scans could differ between app runs).
59
let ticks_since_insert = this_run.relative_to(self).tick.min(MAX_CHANGE_AGE);
60
let ticks_since_system = this_run.relative_to(last_run).tick.min(MAX_CHANGE_AGE);
61
62
ticks_since_system > ticks_since_insert
63
}
64
65
/// Returns a change tick representing the relationship between `self` and `other`.
66
#[inline]
67
pub(crate) fn relative_to(self, other: Self) -> Self {
68
let tick = self.tick.wrapping_sub(other.tick);
69
Self { tick }
70
}
71
72
/// Wraps this change tick's value if it exceeds [`Tick::MAX`].
73
///
74
/// Returns `true` if wrapping was performed. Otherwise, returns `false`.
75
#[inline]
76
pub fn check_tick(&mut self, check: CheckChangeTicks) -> bool {
77
let age = check.present_tick().relative_to(*self);
78
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true
79
// so long as this check always runs before that can happen.
80
if age.get() > Self::MAX.get() {
81
*self = check.present_tick().relative_to(Self::MAX);
82
true
83
} else {
84
false
85
}
86
}
87
}
88
89
/// An observer [`Event`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make
90
/// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus
91
/// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old.
92
///
93
/// # Example
94
///
95
/// Here a schedule is stored in a custom resource. This way the systems in it would not have their change
96
/// ticks automatically updated via [`World::check_change_ticks`](crate::world::World::check_change_ticks),
97
/// possibly causing `Tick`-related bugs on long-running apps.
98
///
99
/// To fix that, add an observer for this event that calls the schedule's
100
/// [`Schedule::check_change_ticks`](bevy_ecs::schedule::Schedule::check_change_ticks).
101
///
102
/// ```
103
/// use bevy_ecs::prelude::*;
104
/// use bevy_ecs::component::CheckChangeTicks;
105
///
106
/// #[derive(Resource)]
107
/// struct CustomSchedule(Schedule);
108
///
109
/// # let mut world = World::new();
110
/// world.add_observer(|check: On<CheckChangeTicks>, mut schedule: ResMut<CustomSchedule>| {
111
/// schedule.0.check_change_ticks(*check);
112
/// });
113
/// ```
114
#[derive(Debug, Clone, Copy, Event)]
115
pub struct CheckChangeTicks(pub(crate) Tick);
116
117
impl CheckChangeTicks {
118
/// Get the present `Tick` that other ticks get compared to.
119
pub fn present_tick(self) -> Tick {
120
self.0
121
}
122
}
123
124
/// Interior-mutable access to the [`Tick`]s for a single component or resource.
125
#[derive(Copy, Clone, Debug)]
126
pub struct TickCells<'a> {
127
/// The tick indicating when the value was added to the world.
128
pub added: &'a UnsafeCell<Tick>,
129
/// The tick indicating the last time the value was modified.
130
pub changed: &'a UnsafeCell<Tick>,
131
}
132
133
impl<'a> TickCells<'a> {
134
/// # Safety
135
/// All cells contained within must uphold the safety invariants of [`UnsafeCellDeref::read`].
136
#[inline]
137
pub(crate) unsafe fn read(&self) -> ComponentTicks {
138
ComponentTicks {
139
// SAFETY: The callers uphold the invariants for `read`.
140
added: unsafe { self.added.read() },
141
// SAFETY: The callers uphold the invariants for `read`.
142
changed: unsafe { self.changed.read() },
143
}
144
}
145
}
146
147
/// Records when a component or resource was added and when it was last mutably dereferenced (or added).
148
#[derive(Copy, Clone, Debug)]
149
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))]
150
pub struct ComponentTicks {
151
/// Tick recording the time this component or resource was added.
152
pub added: Tick,
153
154
/// Tick recording the time this component or resource was most recently changed.
155
pub changed: Tick,
156
}
157
158
impl ComponentTicks {
159
/// Returns `true` if the component or resource was added after the system last ran
160
/// (or the system is running for the first time).
161
#[inline]
162
pub fn is_added(&self, last_run: Tick, this_run: Tick) -> bool {
163
self.added.is_newer_than(last_run, this_run)
164
}
165
166
/// Returns `true` if the component or resource was added or mutably dereferenced after the system last ran
167
/// (or the system is running for the first time).
168
#[inline]
169
pub fn is_changed(&self, last_run: Tick, this_run: Tick) -> bool {
170
self.changed.is_newer_than(last_run, this_run)
171
}
172
173
/// Creates a new instance with the same change tick for `added` and `changed`.
174
pub fn new(change_tick: Tick) -> Self {
175
Self {
176
added: change_tick,
177
changed: change_tick,
178
}
179
}
180
181
/// Manually sets the change tick.
182
///
183
/// This is normally done automatically via the [`DerefMut`](core::ops::DerefMut) implementation
184
/// on [`Mut<T>`](crate::change_detection::Mut), [`ResMut<T>`](crate::change_detection::ResMut), etc.
185
/// However, components and resources that make use of interior mutability might require manual updates.
186
///
187
/// # Example
188
/// ```no_run
189
/// # use bevy_ecs::{world::World, component::ComponentTicks};
190
/// let world: World = unimplemented!();
191
/// let component_ticks: ComponentTicks = unimplemented!();
192
///
193
/// component_ticks.set_changed(world.read_change_tick());
194
/// ```
195
#[inline]
196
pub fn set_changed(&mut self, change_tick: Tick) {
197
self.changed = change_tick;
198
}
199
}
200
201