mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
c8e035d332
Introduce an OPTIONS -> PERFORMANCE setting that scales the port's optional presentation extras down for weaker hardware, so older/lower-end devices can run the game smoothly. The tier governs the three heaviest non-faithful extras -- the 3D TILT, the GBC FX post-process shader, and survey ZOOM (which renders connected neighbor maps) -- plus a hard FPS ceiling. It never touches game logic, which is fixed-step off dt, so every tier plays identically. - src/core/Performance.lua: tiers (auto/high/balanced/low), a conservative device auto-detect (ARM handhelds -> low, phones -> balanced, normal desktops -> high), per-tier caps, and the option-row cycle. Zero requires, like GameVersion. - Game:applyOptions clamps the *live* presentation state against the tier without rewriting stored options, so a lower tier hides the player's TILT/GBC FX/ZOOM/FPS choices and a higher tier restores them exactly. - Zoom.offsetRange floors the range at FIT when survey is disallowed, so the option row, hotkey, and mouse wheel all stop at close-up on LOW. - New save.options.performance default "auto"; OPTIONS row heads the display group and re-applies live. - Tests: tests/engine/performance_tiers.lua (ROM-free); mod_ui_tests row golden updated for the spliced row. AUTO resolves to HIGH on a normal desktop and on every options.lua that predates the option, so the common case is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q6bFAiQyZ5jDmewsbB4LG9
114 lines
4.7 KiB
Lua
114 lines
4.7 KiB
Lua
-- Graphics performance tier (src/core/Performance.lua): the AUTO device
|
|
-- heuristic, tier normalization/labels, per-tier caps, the option-row
|
|
-- cycle, and applyOptions recording the live tier. Pure logic over
|
|
-- stubbed love/jit globals, so it needs no ROM and runs in the T2 tier.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local Performance = require("src.core.Performance")
|
|
|
|
-- detect() reads the live `love` and `jit` globals; swap them per scenario
|
|
-- and restore afterward so nothing leaks between checks (or into luajit).
|
|
local realLove, realJit = love, jit
|
|
|
|
local function device(os, arch, cores)
|
|
love = {
|
|
system = {
|
|
getOS = function() return os end,
|
|
getProcessorCount = function() return cores end,
|
|
},
|
|
}
|
|
jit = arch and { arch = arch } or nil
|
|
end
|
|
|
|
local function restore()
|
|
love, jit = realLove, realJit
|
|
end
|
|
|
|
-- ---------------------------------------------------------------- detect
|
|
device("Linux", "arm64", 4)
|
|
T.eq(Performance.detect(), "low", "ARM64 Linux handheld -> low")
|
|
|
|
device("Linux", "arm", 2)
|
|
T.eq(Performance.detect(), "low", "32-bit ARM Linux handheld -> low")
|
|
|
|
device("Android", "arm64", 8)
|
|
T.eq(Performance.detect(), "balanced", "Android phone -> balanced (not low)")
|
|
|
|
device("iOS", "arm64", 6)
|
|
T.eq(Performance.detect(), "balanced", "iOS -> balanced")
|
|
|
|
device("OS X", "x64", 8)
|
|
T.eq(Performance.detect(), "high", "8-core desktop -> high")
|
|
|
|
device("Windows", "x64", 2)
|
|
T.eq(Performance.detect(), "balanced", "dual-core desktop -> balanced")
|
|
|
|
device("Windows", "x86", 1)
|
|
T.eq(Performance.detect(), "balanced", "single-core desktop -> balanced")
|
|
|
|
-- No love, no jit (a plain-Lua tool/test): nothing looks weak -> high, so
|
|
-- tooling that runs applyOptions is never surprised by a clamp.
|
|
love, jit = nil, nil
|
|
T.eq(Performance.detect(), "high", "no love/jit -> high (no clamping)")
|
|
restore()
|
|
|
|
-- ---------------------------------------------------------------- resolve
|
|
T.eq(Performance.resolve("high"), "high", "resolve passes concrete high")
|
|
T.eq(Performance.resolve("balanced"), "balanced", "resolve passes balanced")
|
|
T.eq(Performance.resolve("low"), "low", "resolve passes low")
|
|
|
|
device("Linux", "arm64", 4)
|
|
T.eq(Performance.resolve("auto"), "low", "resolve auto -> detect (handheld)")
|
|
T.eq(Performance.resolve(nil), "low", "resolve nil -> detect")
|
|
T.eq(Performance.resolve("bogus"), "low", "resolve garbage -> detect")
|
|
restore()
|
|
|
|
-- --------------------------------------------------------------- normalize
|
|
T.eq(Performance.normalize("auto"), "auto", "normalize keeps auto")
|
|
T.eq(Performance.normalize("low"), "low", "normalize keeps low")
|
|
T.eq(Performance.normalize("xyz"), "auto", "normalize garbage -> auto")
|
|
T.eq(Performance.normalize(nil), "auto", "normalize nil -> auto")
|
|
|
|
-- ------------------------------------------------------------------- label
|
|
T.eq(Performance.label("low"), "LOW", "label low")
|
|
T.eq(Performance.label("balanced"), "BALANCED", "label balanced")
|
|
T.eq(Performance.label(nil), "AUTO", "label nil -> AUTO")
|
|
|
|
-- -------------------------------------------------------------------- caps
|
|
local hi, lo = Performance.CAPS.high, Performance.CAPS.low
|
|
T.check(hi.tilt and hi.gbcfx and hi.survey and not hi.fpsMax, "high caps: all on, no fps ceiling")
|
|
T.check((not lo.tilt) and (not lo.gbcfx) and (not lo.survey), "low caps: heavy extras off")
|
|
T.eq(lo.fpsMax, 60, "low caps: FPS ceiling of 60")
|
|
local bal = Performance.CAPS.balanced
|
|
T.check((not bal.tilt) and (not bal.gbcfx) and bal.survey and not bal.fpsMax,
|
|
"balanced caps: no tilt/gbcfx, survey kept, no fps ceiling")
|
|
|
|
device("Linux", "arm64", 4)
|
|
T.eq(Performance.caps("auto"), Performance.CAPS.low, "caps(auto) resolves through detect")
|
|
restore()
|
|
|
|
-- ------------------------------------------------------------- applyOptions
|
|
local caps = Performance.applyOptions({ performance = "low" })
|
|
T.eq(caps, Performance.CAPS.low, "applyOptions returns the resolved caps")
|
|
T.eq(Performance.tier, "low", "applyOptions records the live tier")
|
|
|
|
Performance.applyOptions({ performance = "high" })
|
|
T.eq(Performance.tier, "high", "applyOptions high tier")
|
|
|
|
device("Linux", "arm64", 4)
|
|
Performance.applyOptions(nil)
|
|
T.eq(Performance.tier, "low", "applyOptions(nil) resolves auto to the device tier")
|
|
restore()
|
|
|
|
-- ------------------------------------------------------------------- cycle
|
|
T.eq(Performance.cycle("auto", 1), "high", "cycle auto -> high")
|
|
T.eq(Performance.cycle("high", 1), "balanced", "cycle high -> balanced")
|
|
T.eq(Performance.cycle("balanced", 1), "low", "cycle balanced -> low")
|
|
T.eq(Performance.cycle("low", 1), "auto", "cycle low wraps to auto")
|
|
T.eq(Performance.cycle("auto", -1), "low", "cycle back auto -> low")
|
|
T.eq(Performance.cycle("high", -1), "auto", "cycle back high -> auto")
|
|
|
|
T.finish("performance_tiers")
|