Optimize a pass

Optimize one pass by first deciding what changes every frame.

0. Measure first

Attach a timer(gpu) span (requires init({ requiredFeatures: ["timestamp-query"] })) and judge every change by the reported GPU milliseconds:

TypeScript
1
2
3
const timer = timer(gpu);
timer.onResults((spans) => console.log(`pass ${spans.pass}ms`));
frameLoop(gpu, (f) => f.pass({ target, timer: timer.span("pass") }, (p) => p.draw(effect)));

1. Static commands

If the draw list is static, record it once:

TypeScript
1
2
3
4
5
const effectBundle = bundle(gpu, { target }, (b) => {
  b.draw(background);
  b.draw(grid);
});
frameLoop(gpu, (f) => f.pass(target, (p) => p.bundles(effectBundle)));

2. Animated scalar/vector values

Keep the pass object and write values in place:

TypeScript
1
2
3
4
5
6
const effect = effect(gpu, WGSL, { set: { time: 0, exposure: 1 } });
const time = clock(gpu);
frameLoop(gpu, (f) => {
  effect.set({ time: time.time });
  f.pass(target, effect);
});

3. Resources that swap

Use ping-pong rather than allocating a new target or storage buffer:

TypeScript
1
2
3
4
5
6
const state = pingPong(gpu, 512, 512, { format: "rgba16float" });
frameLoop(gpu, (f) => {
  step.set({ src: state.read.color });
  f.pass(state.write, step);
  state.swap();
});

4. Many objects

Use instancing for many copies of the same draw, or use UniformPool plus draw.group() when every object needs a different uniform block.