Slice Tilt: from 40 seconds of shader compile to 6

· 2 min read · webgpu, three.js, performance, game-dev, batchedmesh

A colorful tilted game board slice with a ball, a scene that takes seconds to compile its shaders on first load

How long Slice Tilt takes to become playable is, on a first visit, almost all shader compilation. This is what was slow and what changed.

Measure with a cold cache #

The driver keeps what it compiled, so the second visit of anything is 30 to 150 times faster than the first. A measurement taken on a machine that has run the page before measures nothing a new player will see. The probe gives every shader a constant that is new in each page load, so the driver cannot answer from its cache.

What was slow (about 40 s) #

  1. Frames ran before the pre-compile did. compileAsync(scene) was called without a camera: it threw, a .catch hid the error, and nothing was compiled ahead. 563 pipelines were made on the render path, one at a time.
  2. One shader per InstancedMesh. three keys instanced meshes on the object's uuid, so 186 instanced meshes with one material were 186 builds and 2.5 MB of WGSL.
  3. The ball's reflection probe draws the slice into a cube, another render context, so 85 more pipelines on the first frames.
  4. compileAsync itself compiles one at a time and yields a lot.

What changed #

  • The canvas does not draw until a custom pre-compile resolves. All pipelines are in flight at once, 16 at a time, including objects out of the frustum, and errors are not swallowed.
  • The probe's cube pass is part of the pre-compile.
  • About 140 InstancedMeshes became one BatchedMesh per kit and two for the dressing.

Results (cold, M2 Max) #

before after
ready 35-39 s 5-7 s
CPU x4, a phone's main thread 43 s 12 s
shader modules 602 210
pipelines on the render path 563 26

A trap worth knowing #

NodeMaterial reads instanceColor while it builds, and three's cache key does not include it. An InstancedMesh that gets its colors later, in the first frame, keeps the shader built without them and draws white, with no warning. Create colors where the mesh is made.

The same family of work on another game is in the Threejs-Punk startup post. The project is on its project page.

Related projects

  • Slice Tilt — Free pinball on a four-slice pizza