Skip to content

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 07 Aug 08:16
· 212 commits to main since this release

This release removes the biggest per-frame allocator cost in the translation layer: dynamic vertex and index buffer backings are now recycled through an in-process pool instead of round-tripping the general allocator. The pool is on by default with a 128 MiB cap, configurable as memory.pageboxPoolCapMB in mtld3d.conf (0 disables it and restores the old behaviour).

The problem it fixes: every contended lock of a DYNAMIC buffer allocates a fresh backing and frees the old one a frame later. Buffers above the allocator's local-cache limit have their pages returned to the OS on every free, so the game's first write into each fresh backing pays zero-fill page faults plus mapping syscalls, and both are roughly an order of magnitude more expensive under Wine and Rosetta than on native Windows. A busy scene moves close to 2 GB/s through this path, most of it one hot ~3 MB buffer renamed once or twice per frame.

With the pool, retired backings are parked per size class and handed back warm to the next same-size lock. Measured on a busy scene: the pool serves 99.8 to 100 percent of requests, allocator-bound backing traffic per 5-second window drops from gigabytes to a few megabytes, the process page-fault rate drops about 50x, and the buffer-lock path falls from several percent of the game thread in sampling profiles to noise. The trade-off is that parked pages stay committed instead of returning to the OS, bounded by the cap; the observed busy-scene peak is 59 MiB, so the default leaves about 2x headroom.

The shipped binaries now also keep frame pointers. The sampling profiler used to diagnose performance reports recovers call stacks by walking the frame-pointer chain, and a build without them stops every walk at the first frame, so a profile taken on a user's machine could name a hot function but not its callers. On 32-bit x86 this costs a general-purpose register out of eight; attributable profiles from the field are worth more than the difference.

Also in this release: the perf summary (available in PERF=1 builds) grew the instrumentation that made the above measurable, including rename byte totals, backing traffic that reaches the global allocator, and a per-window sample of process page faults.

Installing is unchanged, and covered in INSTALL.md inside the bundle.

Full changelog: v0.3.1...v0.4.0