fable/val Public Public: anyone can read this; nobody can change it.

forked from benjivers/val

workbenchthis machine
Files 51e1b3de2c93c9d19656cbce7937019e452ac771
1
/**
2
 * The frame governor, fed frames by hand. A second of slow frames should
3
 * cost one rung and not four; a second of fast ones should not immediately
4
 * give it back, because a resolution that flaps is worse than a low one.
5
 */
6
7
import { expect, test } from "bun:test";
8
import { FAST, makeBudget, measure, PATIENCE, SLOW, WINDOW } from "../static/budget.js";
9
10
/** `seconds` of frames, each `ms` long, and how many times the level moved. */
11
function run(budget: ReturnType<typeof makeBudget>, ms: number, seconds: number, from = 0) {
12
  let at = from;
13
  let moves = 0;
14
  const end = from + seconds;
15
  while (at < end) {
16
    at += ms / 1000;
17
    if (measure(budget, ms, at)) moves++;
18
  }
19
  return { moves, at };
20
}
21
22
test("a second over budget costs exactly one rung, not four", () => {
23
  const budget = makeBudget(0);
24
  const { moves } = run(budget, SLOW + 8, WINDOW * 1.5);
25
  expect(moves).toBe(1);
26
  expect(budget.level).toBe(1);
27
});
28
29
test("frames inside the budget leave the level alone", () => {
30
  const budget = makeBudget(2);
31
  run(budget, (SLOW + FAST) / 2, 10);
32
  expect(budget.level).toBe(2);
33
});
34
35
test("headroom gives a rung back, but not before two windows of it", () => {
36
  const budget = makeBudget(0);
37
  let at = run(budget, SLOW + 8, WINDOW * 1.2).at;
38
  expect(budget.level).toBe(1);
39
  at = run(budget, 8, WINDOW * 1.2, at).at;
40
  expect(budget.level).toBe(1); // one good window is not enough
41
  run(budget, 8, WINDOW * 1.2, at);
42
  expect(budget.level).toBe(0);
43
});
44
45
test("it never refines past the rung the window started on", () => {
46
  const budget = makeBudget(2);
47
  run(budget, 4, 20);
48
  expect(budget.level).toBe(2);
49
  expect(budget.good).toBeLessThanOrEqual(PATIENCE);
50
});
51
52
test("it stops at the coarsest rung rather than walking off the ladder", () => {
53
  const budget = makeBudget(0, 3);
54
  run(budget, 60, 40);
55
  expect(budget.level).toBe(3);
56
});
57
58
test("one slow window, then one fast, does not oscillate", () => {
59
  const budget = makeBudget(0);
60
  let at = 0;
61
  const levels: number[] = [];
62
  for (let i = 0; i < 8; i++) {
63
    at = run(budget, i % 2 === 0 ? SLOW + 8 : 6, WINDOW * 1.05, at).at;
64
    levels.push(budget.level);
65
  }
66
  // Alternating windows never hand a rung back — it takes two good ones —
67
  // so the level climbs slowly and stays put rather than flickering.
68
  expect(levels.filter((l, i) => i > 0 && l < levels[i - 1]!).length).toBe(0);
69
});
70
71
test("the window reports the frame rate and the mean it decided on", () => {
72
  const budget = makeBudget(0);
73
  run(budget, 20, WINDOW * 1.1);
74
  expect(budget.mean).toBeCloseTo(20, 1);
75
  expect(budget.fps).toBeGreaterThan(45);
76
  expect(budget.fps).toBeLessThan(55);
77
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts