Skip to main content

FrameClock

Struct FrameClock 

Source
pub struct FrameClock { /* private fields */ }
Expand description

Fixed-timestep accumulator plus variable render delta.

See the module docs for the loop shape.

Implementations§

Source§

impl FrameClock

Source

pub fn new(tick_hz: u32) -> Self

A clock running the simulation at tick_hz.

§Panics

If tick_hz is zero, or over one tick per nanosecond — the period below is truncating integer nanoseconds, so a rate past 1 GHz rounds to a period of zero and there is no clock to build. It used to reach with_period and fail there, blaming a period the caller never passed.

Source

pub fn with_period(period: Duration) -> Self

A clock with an explicit tick period.

§Panics

If period is zero.

Source

pub const fn set_max_catch_up_ticks(&mut self, ticks: u32)

Caps how many ticks a single FrameClock::update may queue.

This is the spiral-of-death guard. If a frame takes longer than the ticks it queues take to simulate, the accumulator grows, which queues more ticks, which take longer still — the simulation never catches up and the game locks solid. Past the cap, surplus wall-clock time is discarded: those ticks never run, so simulation time falls permanently behind wall time (a hitch is a hitch — nothing rewinds it), but the frame rate recovers instead of collapsing. The count of skipped ticks is available from FrameClock::dropped_ticks and is worth logging; in a multiplayer build it is the client’s cue that it has desynced from the server’s tick timeline.

The default, DEFAULT_MAX_CATCH_UP_TICKS, allows 8 ticks — 133 ms of catch-up at 60 Hz, enough to absorb an asset-load or shader-compile hitch without letting a slow machine dig itself into a hole.

§Panics

If ticks is zero — a clock that can never tick is never what you want.

Source

pub fn update(&mut self, now: Duration)

Feeds the clock the current timestamp. Call once per frame, before the tick loop.

Timestamps must come from a TimeSource. A timestamp that moves backwards (a source that is not actually monotonic) contributes zero time and bumps FrameClock::backwards_updates rather than panicking or wrapping.

Source

pub const fn consume_tick(&mut self) -> bool

Takes one pending tick, if the accumulator holds a whole one.

Drive it as while clock.consume_tick() { server_tick(); }.

Source

pub const fn pending_ticks(&self) -> u32

How many ticks FrameClock::consume_tick would still yield.

Source

pub const fn next_tick_due(&self) -> Option<Duration>

When the next tick comes due, on the timeline FrameClock::update is fed: the last update, plus whatever the accumulator still lacks of a whole tick. A tick already pending is due at the last update. None before the first update, which has no timeline to answer on.

What a loop that has nothing to render may sleep until: no tick is due before this, so a wait that ends here keeps the simulation on schedule.

Source

pub const fn tick(&self) -> TickId

The id of the most recently simulated tick.

Source

pub const fn tick_dt(&self) -> Duration

The fixed simulation step. Every tick advances the world by exactly this much, whatever the frame rate.

Source

pub fn tick_dt_secs(&self) -> f64

FrameClock::tick_dt in seconds, for integrators.

Source

pub const fn render_dt(&self) -> Duration

Wall-clock time covered by the last FrameClock::update — the variable render delta, for camera smoothing, UI animation and anything else that is not simulation.

Source

pub fn render_dt_secs(&self) -> f32

Source

pub fn alpha(&self) -> f32

How far the render frame sits between the last simulated tick and the next one, in [0, 1).

Interpolated rendering wants lerp(previous_state, current_state, alpha). Call it after draining the tick loop; called before, the accumulator may still hold whole ticks, in which case this saturates just below 1.0 rather than reporting a fraction greater than one.

Source

pub const fn dropped_ticks(&self) -> u64

Ticks discarded by the catch-up cap since the clock was created.

Source

pub const fn backwards_updates(&self) -> u64

Number of FrameClock::update calls whose timestamp went backwards. Non-zero means the time source is broken.

Trait Implementations§

Source§

impl Debug for FrameClock

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.