Skip to main content

lumen_core/
tick.rs

1//! Main-world tick stages and the [`Tick`] resource.
2//!
3//! - Each stage is a `bevy_ecs` [`SystemSet`].
4//! - Ordering is enforced by `.chain()` in [`crate::app::App::new`].
5//! - The render schedule runs after the main schedule and the extract step; see [`crate::render_world`].
6
7use bevy_ecs::prelude::*;
8use std::time::{Duration, Instant};
9
10/// The five ordered main-world stages of a Lumen tick.
11#[derive(SystemSet, Clone, Copy, Debug, Hash, PartialEq, Eq)]
12pub enum TickStage {
13    /// Ingests OS events (keyboard, mouse, IME, window). Window backend writes here.
14    Input,
15    /// Drains the bounded [`crate::command::CommandQueue`] and applies deferred mutations.
16    CommandDrain,
17    /// Runs application systems: state mutation, animations, scripts.
18    Systems,
19    /// Runs the layout engine: dirty flush, taffy round-trip, absolute-coord write-back.
20    LayoutSync,
21    /// Computes the accessibility-tree diff and pushes it to the OS.
22    A11ySync,
23}
24
25/// Per-tick frame clock resource.
26///
27/// - [`Self::now`] is captured at the start of each [`crate::app::App::tick`] before the [`TickStage::Input`] systems run.
28/// - [`Self::dt`] is `now - previous_now` (zero on the first tick).
29/// - [`Self::frame`] is a monotonic counter incremented once per tick (starts at 0; reaches 1 on the first tick).
30///
31/// Wave 1 migrates the animation primitives off `std::time::Instant::now()` to read this resource so headless tests can
32/// drive deterministic frame clocks; foundation only installs and updates the resource.
33#[derive(Resource, Clone, Copy, Debug)]
34pub struct Tick {
35    /// Wall-clock instant captured at the start of the current tick.
36    pub now: Instant,
37    /// Elapsed time since the previous tick's [`Self::now`]. Zero on the first tick.
38    pub dt: Duration,
39    /// Monotonic tick counter; 0 before the first tick, 1 after, and so on.
40    pub frame: u64,
41}
42
43impl Default for Tick {
44    fn default() -> Self {
45        Self {
46            now: Instant::now(),
47            dt: Duration::ZERO,
48            frame: 0,
49        }
50    }
51}
52
53impl Tick {
54    /// Advances the clock by capturing a fresh `Instant::now()` and bumping [`Self::frame`].
55    /// Called by [`crate::app::App::tick`] at the top of each tick, before the main schedule runs.
56    pub fn advance(&mut self) {
57        let now = Instant::now();
58        self.dt = now.saturating_duration_since(self.now);
59        self.now = now;
60        self.frame = self.frame.wrapping_add(1);
61    }
62}