benjivers/val Public Public: anyone can read this; the crew can change it.
Files 8cd8fa48c9222fcda5fee40624ca20b80aa95988
| 1 | /** |
| 2 | * The frame budget: what the last second of frames cost, and one knob. |
| 3 | * |
| 4 | * The renderer's price is the number of columns it draws, and the right |
| 5 | * number is not knowable from here — it depends on the machine, the window, |
| 6 | * the battery and whatever else the browser is doing. So it is measured: a |
| 7 | * second of frames at a time, and if the mean frame is over the budget the |
| 8 | * world is drawn a rung coarser (see resolution.js), and a rung finer again |
| 9 | * when there is room. |
| 10 | * |
| 11 | * It must not flap, because a resolution changing twice a second is more |
| 12 | * visible than a resolution that is simply low. Three things stop it: |
| 13 | * coarsening and refining have different thresholds, refining needs two |
| 14 | * windows of headroom in a row, and it never refines past the rung the |
| 15 | * window started on. |
| 16 | * |
| 17 | * Measurement only. Nothing here is sent anywhere or told to the simulation. |
| 18 | */ |
| 19 | |
| 20 | /** Seconds of frames behind one decision. */ |
| 21 | export const WINDOW = 1; |
| 22 | /** Mean milliseconds a frame: over this is over budget, under this has room. */ |
| 23 | export const SLOW = 20; |
| 24 | export const FAST = 13; |
| 25 | /** Windows of headroom before a rung is given back. */ |
| 26 | export const PATIENCE = 2; |
| 27 | |
| 28 | export function makeBudget(base = 0, max = 6) { |
| 29 | return { base, max, level: base, frames: 0, total: 0, since: 0, good: 0, fps: 0, mean: 0 }; |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * One frame, `ms` long, drawn at `now` seconds. Returns true when the level |
| 34 | * moved — the caller resizes what it draws into — and updates `fps` and |
| 35 | * `mean` at the end of every window, for the readout to print. |
| 36 | */ |
| 37 | export function measure(budget, ms, now) { |
| 38 | budget.frames++; |
| 39 | budget.total += ms; |
| 40 | if (budget.since === 0) { |
| 41 | budget.since = now; |
| 42 | return false; |
| 43 | } |
| 44 | const span = now - budget.since; |
| 45 | if (span < WINDOW) return false; |
| 46 | budget.mean = budget.total / budget.frames; |
| 47 | budget.fps = budget.frames / span; |
| 48 | budget.frames = 0; |
| 49 | budget.total = 0; |
| 50 | budget.since = now; |
| 51 | if (budget.mean > SLOW) { |
| 52 | budget.good = 0; |
| 53 | if (budget.level >= budget.max) return false; |
| 54 | budget.level++; |
| 55 | return true; |
| 56 | } |
| 57 | if (budget.mean < FAST && budget.level > budget.base) { |
| 58 | budget.good++; |
| 59 | if (budget.good < PATIENCE) return false; |
| 60 | budget.good = 0; |
| 61 | budget.level--; |
| 62 | return true; |
| 63 | } |
| 64 | budget.good = 0; |
| 65 | return false; |
| 66 | } |