Run Lua GC in parallel with the rest of the frame

Closes #9043 (closed).

LuaManager::update() ran a fixed lua_gc(LUA_GCSTEP, 100) at its start. GC now runs on the Lua worker thread :

  • GC step is split out of update() into LuaManager::gcStep()
  • MWLua::Worker has a new gc() and finishGc() pair of methods. gc() starts a loop of small steps (10 units per lua_gc call) on the worker thread; the loop stops at finishGc() or when a full collection cycle completes. No behaviour change if there's no Lua thread
  • collection starts in engine.cpp just after finishUpdate() and stops it at the top of the next frame's synchronized update; collection overlaps the frame tail, the framerate-limiter sleep and the next frame's input and sound updates. finishGc() waits only for the step in flight

gc steps per frame = 0 still disables collection entirely; docs updated.

I ran a sweep to check if the arbitrary "10 loops" value changed anything if increased/decreased, but it appears bounded by the collector's atomic phase rather than by the step size.

With a fresh save in Balmora, 1080p on an laptop with i5-8365U and Intel UHD 620 (Mesa), A = master and B = this change :

Synthetic pressure (global script allocating 1500 tables + unique strings per frame):

metric A B
Lua update p99 7.68 ms 6.11 ms
frame p95 10.4 ms 9.7 ms
frame p99 11.5 ms 11.0 ms

Real mods from #9043 (closed) (MercyCAO, AnimatedLanterns, SneakIsGoodNow, DynamicCamera, ReAnimation, LuaPhysics):

metric A B
Lua update p99 9.64 ms 3.44 ms
average fps 101.0 105.4
frame p99 14.6 ms 14.3 ms

Testing done :

  • The A/B/A/B benches, on Linux w/ KWin Wayland.

  • Repeated save loads, menu idle and clean shutdowns on both binaries across runs; no issues seen.

  • Wandering around a bit, loading new cells, talking to NPCs, etc.

  • Checked runtime logs for new errors, nothing to report.

  • I have read the CONTRIBUTING guidelines

  • My code runs locally

  • My code passes CI on my fork

Open point: if the background loop cannot complete cycles under extreme allocation rates, I assume LuaJIT will force collection during script allocations. I didnt implement a fallback (like forcing steps when no cycle completes for N frames) because I couldn't repro that case; happy to add it here if you'd prefer.

Merge request reports

Loading
Loading