Skip to content
Devlog

Choosing the engine's stack, in Low Power Mode

An early test scene from September 29: rows of loose concrete pieces on flat ground

The question

The first stage of the project was to find out whether a Swift, Metal and Jolt stack would hold up under building destruction. We set the constraints: Mac only, no Unity, and structural destruction in place of voxels. The session on Stage 1 compiled the Jolt physics library from source through Swift’s package manager, wrote a benchmark harness, and set numbered gates before measuring anything. A gate is a limit a scenario has to stay under. If a gate in the first group failed, work would stop until we decided what to do with the numbers.

The budget came from an M4 Mac: 8 ms for physics and 11 ms for graphics inside a 16.7 ms frame. Gates run on the M5 the game is built on use 0.75 of that, an assumed margin until an M4 is measured.

Two checks came first. The package-manager build of Jolt ran at 92.5 to 99.5% of the throughput of Jolt’s own optimized build, depending on thread count, so later numbers could be trusted. And a single-pass graphics layout that keeps its intermediate buffers in on-chip memory took 2.2 ms against 8.7 ms for a two-pass layout at 2880 × 1800.

Throttled by accident, kept on purpose

The benchmark run recorded for the first verdict turned out to have run in macOS Low Power Mode, on battery. That mode slows the processor and graphics chip to save power. We decided it was a better baseline because it leaves room, released on those numbers and kept Low Power Mode as the reference for every gate. A gate that passes throttled has headroom at full power and on a slower machine. Every result records the mode, and full-power runs count as information only.

The first verdict

The recorded run, in Low Power Mode, passed every physics gate in the first group.

Gate What it measures Limit Measured
G1 2,000 awake fragments, 95th percentile (p95) 6 ms 1.67 ms on a building, 1.75 ms on terrain
G2 10,000 sleeping fragments, per step 0.75 ms 0.001 ms
G4 Six buildings, one collapsing, no renderer, p95 6 ms 1.99 ms
G5 One piece detaching, p95 1.5 ms 1.21 ms
G8 One 16 × 16 m crater edit 1 ms 0.185 ms

p95 means 95% of steps were faster than that figure. The record’s conclusion was that the engine decision was final. The changelog dates all eight releases, 0.1.0 through 0.8.0, to September 29.

The Stage 1 close

Closing Stage 1 meant running the remaining gates in Low Power Mode. Four failed.

Gate Limit First Low Power Mode run Later the same day
G3, graphics for six buildings and 20,000 fragments, p95 8 ms 9.97 ms 7.01 to 7.63 ms
G4, full frame through a collapse, p95 12.5 ms 24.7 ms 8.86 and 10.39 ms
G6, load calculation for a 20,000-piece structure, p95 1 ms 1.68 ms 0.808 ms
G7, one 64-cell fracture on one worker, p95 3 ms 4.58 ms 4.19 ms, still over

G3 came down after two rendering changes: the distant shadow layers now alternate frames, and terrain detail follows how large the error would look on screen. G6 came down after a term in the load solver was simplified. G7 stayed over. We decided to keep 64 fracture cells at the high tier, and had the game detect Low Power Mode and adjust the way it would on a slower machine, while the high tier keeps being optimized. The game now switches to its low tier in Low Power Mode. The low tier fractures at 24 cells, which takes 1.32 ms p95.

The wrong diagnosis

The slow collapse frame was the largest failure. Physics steps took about 20.5 ms while the charges fired, and 2 to 4 ms once they stopped.

The first diagnosis, made with the Low Power Mode run, blamed the fracture work, on the theory that the background workers that break pieces apart were taking cores from the physics step. It was corrected about 13 minutes later. The 18 fractures took 2 to 5 ms each on a worker, and the steps between charges were just as slow. A profiler trace of the window put the time in two places. One was collision detection against the terrain, including the slow path Jolt takes when two shapes are deep inside each other, which happens when fragments are pressed into the ground. The other was solving contacts.

Later that day, after the rubble-weight, crushing and settling work, the same demo cost 0.90 to 1.24 ms p50 per physics step over its first two seconds, and the collapse frame was 8.86 and 10.39 ms p95. Which change removed the 20.5 ms steps was not isolated.

Some slow steps remain, 16 to 40 over 8 ms in a run of about 1,900, in short bursts. Two runs of the same simulation put them at different steps, and changing the number of job threads made no difference beyond the noise. The record reads them as scheduling, outside the simulation’s work. With the display asleep, two runs had 3 and 6.

Provenance

How this post was made

Drafted by
A Claude Code session in the trilithon.games repository, October 10, 2026, working from the game's changelog, docs and tracker.
What it did
Drafted this post from the engine decision record, the changelog and the tracker's comments, and checked each number against them.
Reviewed by
Alex van Rossum,

On the roadmap: The engine verdict

Linear MIP2-202, MIP2-207, MIP2-208, MIP2-209