Core · class

StepBudget

Explained in The loop.

class StepBudget<Label = string>
import { StepBudget } from '@driftengine/core';

Constructor

new

constructor(options?: StepBudgetOptions)
ParameterTypeDescription
options?StepBudgetOptions

Accessors

NameTypeDescription
estimategetnumberWhat one more stop is currently assumed to cost. Decays; see StepBudgetOptions.forget.
worstgetnumberThe longest single stop ever taken. Does not decay: this is the report, not the guard.
worstIngetLabel | nullThe label the caller gave the longest stop.
More

A budget being blown is useless information without the name of the unit that blew it. That is the whole reason next returns a label rather than a boolean: "the worst stop was 57 ms" sends a reader nowhere, and "the worst stop was 57 ms, in upload" sends them to one file.

Methods

spend

spend(budgetMs: number, next: () => Label | null): number

Advance the caller's work for up to budgetMs, and say how many stops that took.

ParameterTypeDescription
budgetMsnumber
next() => Label | null
More

next performs exactly one indivisible unit and returns a label naming what it did, or null when there is nothing left to do — which ends the call without counting a stop.

Return the label of the work just done, not of the work about to be done. The two are easy to confuse where a job names its own next phase, and the report that results sends a reader to the wrong builder every time the phase changes.

The two rules the loop is made of, both arrived at by getting them wrong first:

  • Do not start what you cannot finish. A driver that keeps going while any budget remains overshoots by a whole stop. Measured by a consumer at 30 Hz: 8.3 ms of budget plus an 8.6 ms stop against a frame of 16.7.
  • But always do one. Without the unconditional first stop, a unit costing more than the whole budget means nothing ever advances at all, and a world that never loads is worse than a frame that runs long.