Files
DramaticShape 702d8049f0 initial commit
2026-07-26 16:43:53 -04:00

62 lines
1.9 KiB
Lua

-- Voxel world mode: the cooperative build budget.
--
-- Mesh building runs inside a plain coroutine that ChunkMesher pumps for
-- a few milliseconds a frame (there is no worker thread: LOVE's graphics
-- objects are main-thread only, and the build is pure Lua that slices
-- cleanly). The long loops in Structures and ChunkMesher call tick() as
-- they go; when the frame's slice is spent, tick() suspends the build
-- coroutine and the frame carries on rendering whatever is already
-- cached.
--
-- tick() is deliberately safe to call from ANY context: outside the pump
-- (headless tests, the pure geometry API, a driver poking a build
-- function directly) it recognises it is not inside the build coroutine
-- and does nothing, so synchronous callers keep their synchronous
-- behavior.
local B = { n = 0 }
local clock = (love and love.timer and love.timer.getTime) or os.clock
local deadline = math.huge
local buildCo = nil
-- Enter/leave a pumped slice. `co` is the coroutine being resumed, so
-- tick() can tell the build apart from any other coroutine the engine
-- happens to be running (drivers are coroutines too).
function B.begin(co, seconds)
buildCo = co
deadline = clock() + seconds
end
function B.finish()
buildCo = nil
deadline = math.huge
end
function B.expired()
return clock() > deadline
end
-- Cheap enough to sprinkle through inner loops: one modulo most calls,
-- a clock read every 32nd.
function B.tick()
local n = B.n + 1
B.n = n
if n % 32 ~= 0 then return end
if buildCo and coroutine.running() == buildCo and clock() > deadline then
coroutine.yield("budget")
end
end
-- Like tick() but consults the clock every call -- for coarse loops
-- whose single iteration already costs milliseconds (mesh upload
-- slices), where tick()'s 1-in-32 sampling would never fire.
function B.check()
if buildCo and coroutine.running() == buildCo and clock() > deadline then
coroutine.yield("budget")
end
end
return B