mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-18 03:35:56 +02:00
Frame Cap (#124)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
-- Render frame-rate cap. With a driver control panel forcing
|
||||
-- vsync off, the 160x144 game is trivially cheap and love.run will present
|
||||
-- thousands of frames a second; over hours that cooks the graphics driver
|
||||
-- until a restart, and it wastes power whenever the window is left open in
|
||||
-- the background. A hard cap bounds the present rate. Render-only: game
|
||||
-- logic is fixed-step off dt (src/core/FixedStep.lua), so pacing present()
|
||||
-- changes nothing about timing, audio, or determinism.
|
||||
--
|
||||
-- Persisted as save.options.fpsCap; applied from OptionsMenu and on boot
|
||||
-- via Game:applyOptions. main.lua's love.run reads FrameCap.current each
|
||||
-- frame for its sleep budget. The module never touches love.timer itself,
|
||||
-- so it stays safe under the headless test stub.
|
||||
|
||||
local FrameCap = {}
|
||||
|
||||
-- Selectable steps: the normal framerate stops between the floor and the
|
||||
-- ceiling. STEPS[1] == MIN and STEPS[#STEPS] == MAX, so the nearest-step
|
||||
-- snap in normalize doubles as the clamp. Cycling past the last wraps.
|
||||
FrameCap.STEPS = { 30, 40, 50, 60, 75, 90, 100, 120, 144, 160 }
|
||||
FrameCap.MIN = 30
|
||||
FrameCap.MAX = 160
|
||||
FrameCap.DEFAULT = 60
|
||||
|
||||
-- The live cap the run loop paces to. Defaults so the launcher and the
|
||||
-- save editor are paced before any save applies its stored option.
|
||||
FrameCap.current = FrameCap.DEFAULT
|
||||
|
||||
-- Nearest valid step for an arbitrary value (a hand-edited options.lua or
|
||||
-- an old save with no fpsCap key), so a bad number degrades to something
|
||||
-- sane; nil / non-numbers fall back to the default. A value below MIN or
|
||||
-- above MAX snaps to that end, since MIN/MAX are the first/last steps.
|
||||
function FrameCap.normalize(value)
|
||||
value = tonumber(value)
|
||||
if not value then return FrameCap.DEFAULT end
|
||||
local best, bestDiff = FrameCap.DEFAULT, math.huge
|
||||
for _, step in ipairs(FrameCap.STEPS) do
|
||||
local diff = math.abs(step - value)
|
||||
if diff < bestDiff then best, bestDiff = step, diff end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
-- plain numeric text for the options row (e.g. "60")
|
||||
function FrameCap.label(value)
|
||||
return tostring(FrameCap.normalize(value))
|
||||
end
|
||||
|
||||
-- cycle to the next/previous step, wrapping (the options row idiom)
|
||||
function FrameCap.cycle(value, dir)
|
||||
local steps = FrameCap.STEPS
|
||||
local snapped = FrameCap.normalize(value)
|
||||
local cur = 1
|
||||
for i, step in ipairs(steps) do
|
||||
if step == snapped then cur = i break end
|
||||
end
|
||||
local nextIdx = (cur - 1 + (dir or 1)) % #steps + 1
|
||||
return steps[nextIdx]
|
||||
end
|
||||
|
||||
-- Store the chosen cap as the live value the run loop paces to. Never
|
||||
-- touches love.timer, so it is safe headless -- the loop just reads the
|
||||
-- number back. Returns the normalized value it stored.
|
||||
function FrameCap.apply(value)
|
||||
FrameCap.current = FrameCap.normalize(value)
|
||||
return FrameCap.current
|
||||
end
|
||||
|
||||
function FrameCap.applyOptions(opts)
|
||||
FrameCap.apply(opts and opts.fpsCap)
|
||||
end
|
||||
|
||||
return FrameCap
|
||||
@@ -445,6 +445,9 @@ function Game:applyOptions(opts)
|
||||
require("src.render.Tilt").applyOptions(opts)
|
||||
require("src.render.GBCFX").applyOptions(opts)
|
||||
require("src.core.VideoMode").applyOptions(opts)
|
||||
-- normalizes a nil/garbage cap to the 60 default, so old saves with no
|
||||
-- fpsCap key pace at the standard rate (issue #88)
|
||||
require("src.core.FrameCap").applyOptions(opts)
|
||||
Input:applyBindings(opts.bindings)
|
||||
end
|
||||
|
||||
|
||||
@@ -192,6 +192,8 @@ function SaveData.defaultOptions()
|
||||
gbcfx = 0,
|
||||
-- windowed | borderless (desktop fullscreen); ignored on mobile
|
||||
videoMode = "windowed",
|
||||
-- hard render frame-rate cap; render-only pacing (issue #88, FrameCap.lua)
|
||||
fpsCap = 60,
|
||||
-- Native mod enablement is an installation option, not save-slot data.
|
||||
-- Missing entries mean enabled so newly installed mods work by default.
|
||||
mods = {},
|
||||
|
||||
Reference in New Issue
Block a user