/** * The frame budget: what the last second of frames cost, and one knob. * * The renderer's price is the number of columns it draws, and the right * number is not knowable from here — it depends on the machine, the window, * the battery and whatever else the browser is doing. So it is measured: a * second of frames at a time, and if the mean frame is over the budget the * world is drawn a rung coarser (see resolution.js), and a rung finer again * when there is room. * * It must not flap, because a resolution changing twice a second is more * visible than a resolution that is simply low. Three things stop it: * coarsening and refining have different thresholds, refining needs two * windows of headroom in a row, and it never refines past the rung the * window started on. * * Measurement only. Nothing here is sent anywhere or told to the simulation. */ /** Seconds of frames behind one decision. */ export const WINDOW = 1; /** Mean milliseconds a frame: over this is over budget, under this has room. */ export const SLOW = 20; export const FAST = 13; /** Windows of headroom before a rung is given back. */ export const PATIENCE = 2; export function makeBudget(base = 0, max = 6) { return { base, max, level: base, frames: 0, total: 0, since: 0, good: 0, fps: 0, mean: 0 }; } /** * One frame, `ms` long, drawn at `now` seconds. Returns true when the level * moved — the caller resizes what it draws into — and updates `fps` and * `mean` at the end of every window, for the readout to print. */ export function measure(budget, ms, now) { budget.frames++; budget.total += ms; if (budget.since === 0) { budget.since = now; return false; } const span = now - budget.since; if (span < WINDOW) return false; budget.mean = budget.total / budget.frames; budget.fps = budget.frames / span; budget.frames = 0; budget.total = 0; budget.since = now; if (budget.mean > SLOW) { budget.good = 0; if (budget.level >= budget.max) return false; budget.level++; return true; } if (budget.mean < FAST && budget.level > budget.base) { budget.good++; if (budget.good < PATIENCE) return false; budget.good = 0; budget.level--; return true; } budget.good = 0; return false; }