mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-16 08:11:35 +02:00
Merge pull request #524 from kaosregulator/claude/multi-game-low-end-support-o16gz5
Claude/multi game low end support and some fixes o16gz5
This commit is contained in:
@@ -634,6 +634,23 @@ function Game: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)
|
||||
-- Scale the optional presentation extras to the device's performance
|
||||
-- tier. Every heavy feature was just applied from the stored options
|
||||
-- above; here we clamp the *live* state down for a weaker device without
|
||||
-- rewriting what the player saved, so raising the tier later restores
|
||||
-- their exact TILT / GBC FX / ZOOM / MAX FPS choices. A HIGH tier (the
|
||||
-- default on a normal desktop, and every options.lua predating this
|
||||
-- option) clamps nothing, so it is a no-op for the common case.
|
||||
local caps = require("src.core.Performance").applyOptions(opts)
|
||||
if not caps.tilt then require("src.render.Tilt").setLevel(0) end
|
||||
if not caps.gbcfx then require("src.render.GBCFX").setLevel(0) end
|
||||
local Zoom = require("src.render.Zoom")
|
||||
Zoom.allowSurvey = caps.survey
|
||||
if not caps.survey and Zoom.offset < 0 then Zoom.offset = 0 end
|
||||
if caps.fpsMax then
|
||||
local FrameCap = require("src.core.FrameCap")
|
||||
if FrameCap.current > caps.fpsMax then FrameCap.apply(caps.fpsMax) end
|
||||
end
|
||||
Input:applyBindings(opts.bindings)
|
||||
TouchControls:applyOptions(opts)
|
||||
-- heal soft-bricked APK installs that already saved gbcfx > 0 (#136)
|
||||
|
||||
+8
-1
@@ -23,6 +23,13 @@ local volumeScale = 1
|
||||
local FILTER_HIGHGAIN = { 0.4, 0.16, 0.064 }
|
||||
local filterLevel = 0
|
||||
|
||||
-- Forward-declared here so applyVolume (below) closes over the real playback
|
||||
-- state rather than a nil global: the table literal is assigned further down,
|
||||
-- but a `local state = {}` there would leave every reference above it bound
|
||||
-- to the global `state`. Before this, registering the `music.volume` mod
|
||||
-- hook crashed applyVolume on `state.current` (a nil index).
|
||||
local state
|
||||
|
||||
local function applyVolume(src)
|
||||
if not src then return end
|
||||
local vol = VOLUME * volumeScale
|
||||
@@ -63,7 +70,7 @@ local function applyFilter(src)
|
||||
end
|
||||
end
|
||||
|
||||
local state = {
|
||||
state = {
|
||||
current = nil, -- song label
|
||||
chip = false, -- the playing song is a synthesized channel program
|
||||
source = nil, -- currently playing source
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
-- Graphics performance tier: one knob that scales the port's optional,
|
||||
-- non-faithful presentation extras down for weaker hardware.
|
||||
--
|
||||
-- The heavy extras are all things the Game Boy never had and this port
|
||||
-- adds on top: the whole-screen 3D TILT (transforms the entire map as a
|
||||
-- ground plane), the GBC FX post-process shader (a fullscreen pass), and
|
||||
-- survey ZOOM (zooming out renders the connected neighbor maps -- a lot of
|
||||
-- extra overdraw). A hard FPS ceiling caps present cost on top of that.
|
||||
-- None of this touches game logic, which is fixed-step off dt
|
||||
-- (src/core/FixedStep.lua), so every tier plays identically; they differ
|
||||
-- only in how much optional eye-candy the renderer is allowed to do.
|
||||
--
|
||||
-- The tier is persisted as save.options.performance:
|
||||
-- auto pick a default from the device (see detect())
|
||||
-- high everything on -- the historical behavior
|
||||
-- balanced no TILT, no GBC FX (kept: survey zoom, colors, uncapped FPS)
|
||||
-- low no TILT, no GBC FX, no survey zoom, FPS capped
|
||||
--
|
||||
-- AUTO only chooses the *default* for the current device; every tier is
|
||||
-- selectable in OPTIONS, so a heuristic that guesses wrong is one row away
|
||||
-- from being overridden. The clamps are applied live in Game:applyOptions
|
||||
-- against the stored options without rewriting them, so raising the tier
|
||||
-- restores exactly the TILT / GBC FX / ZOOM / FPS the player had.
|
||||
--
|
||||
-- Zero requires: loads during love.conf and under plain Lua for tools and
|
||||
-- tests, the same way src/core/GameVersion.lua does.
|
||||
|
||||
local Performance = {}
|
||||
|
||||
-- Option-row order (auto first, then most to least capable).
|
||||
Performance.TIERS = { "auto", "high", "balanced", "low" }
|
||||
|
||||
Performance.LABELS = {
|
||||
auto = "AUTO",
|
||||
high = "HIGH",
|
||||
balanced = "BALANCED",
|
||||
low = "LOW",
|
||||
}
|
||||
|
||||
-- What each concrete tier permits. `auto` is resolved to one of these
|
||||
-- before caps are read, so it has no row here. fpsMax = false means no
|
||||
-- extra ceiling (the player's own MAX FPS still applies).
|
||||
Performance.CAPS = {
|
||||
high = { tilt = true, gbcfx = true, survey = true, fpsMax = false },
|
||||
balanced = { tilt = false, gbcfx = false, survey = true, fpsMax = false },
|
||||
low = { tilt = false, gbcfx = false, survey = false, fpsMax = 60 },
|
||||
}
|
||||
|
||||
-- Live resolved tier (never "auto"); Game:applyOptions sets it and the
|
||||
-- renderer / options row read it back. Defaults to the unclamped tier so
|
||||
-- a pre-boot launcher behaves exactly as it always did.
|
||||
Performance.tier = "high"
|
||||
|
||||
local function loveOS()
|
||||
if love and love.system and love.system.getOS then
|
||||
return love.system.getOS()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function processorCount()
|
||||
if love and love.system and love.system.getProcessorCount then
|
||||
return love.system.getProcessorCount()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- CPU architecture via LuaJIT's jit.arch when present: "arm"/"arm64" on
|
||||
-- phones and PortMaster handhelds, "x86"/"x64" on desktops. nil under a
|
||||
-- plain-Lua tool or test with no jit table, which resolves to HIGH (no
|
||||
-- clamping) so tooling is never surprised.
|
||||
local function cpuArch()
|
||||
return (jit and jit.arch) or nil
|
||||
end
|
||||
|
||||
-- Default tier for the current device. Deliberately conservative: only
|
||||
-- the platforms that are reliably weak drop below HIGH, and AUTO is always
|
||||
-- overridable, so a wrong guess costs one OPTIONS row. A normal
|
||||
-- multi-core desktop -- the common case, and every existing install whose
|
||||
-- options.lua predates this option -- resolves to HIGH and behaves exactly
|
||||
-- as before.
|
||||
function Performance.detect()
|
||||
local os = loveOS()
|
||||
local arch = cpuArch()
|
||||
local isArm = arch == "arm" or arch == "arm64"
|
||||
local cores = processorCount()
|
||||
|
||||
-- PortMaster-style ARM Linux handhelds (e.g. the RG34XXSP the project
|
||||
-- already ships a build for): the weakest target here.
|
||||
if isArm and os ~= "Android" and os ~= "iOS" then
|
||||
return "low"
|
||||
end
|
||||
-- Phones and tablets: GBC FX is already force-disabled here (issue #136);
|
||||
-- balanced additionally drops the 3D tilt, the heaviest remaining extra.
|
||||
if os == "Android" or os == "iOS" then
|
||||
return "balanced"
|
||||
end
|
||||
-- Desktop: a dual-core (or single-core) box is the low-end line.
|
||||
if cores and cores <= 2 then
|
||||
return "balanced"
|
||||
end
|
||||
return "high"
|
||||
end
|
||||
|
||||
-- Fold a stored option value to a concrete tier, resolving "auto" (and any
|
||||
-- hand-edited garbage) through detect().
|
||||
function Performance.resolve(value)
|
||||
if value == "high" or value == "balanced" or value == "low" then
|
||||
return value
|
||||
end
|
||||
return Performance.detect()
|
||||
end
|
||||
|
||||
-- Normalize a stored value to a valid option id (keeps "auto"); a bad value
|
||||
-- degrades to auto so a corrupt options.lua still lands on something sane.
|
||||
function Performance.normalize(value)
|
||||
for _, id in ipairs(Performance.TIERS) do
|
||||
if value == id then return id end
|
||||
end
|
||||
return "auto"
|
||||
end
|
||||
|
||||
-- Row label for a stored value: the tier's own name (AUTO / HIGH / ...).
|
||||
function Performance.label(value)
|
||||
return Performance.LABELS[Performance.normalize(value)]
|
||||
end
|
||||
|
||||
-- Concrete caps for a stored option value (resolving auto).
|
||||
function Performance.caps(value)
|
||||
return Performance.CAPS[Performance.resolve(value)]
|
||||
end
|
||||
|
||||
-- Resolve the tier for these options, record it live, and return its caps.
|
||||
-- Called from Game:applyOptions, which clamps the live presentation modules
|
||||
-- against the returned caps.
|
||||
function Performance.applyOptions(opts)
|
||||
local tier = Performance.resolve(opts and opts.performance)
|
||||
Performance.tier = tier
|
||||
return Performance.CAPS[tier]
|
||||
end
|
||||
|
||||
-- Cycle the OPTIONS row: auto -> high -> balanced -> low -> auto (dir -1
|
||||
-- reverses). Lua's `%` is non-negative for a positive modulus, so a
|
||||
-- negative dir wraps correctly.
|
||||
function Performance.cycle(value, dir)
|
||||
value = Performance.normalize(value)
|
||||
local i = 1
|
||||
for idx, id in ipairs(Performance.TIERS) do
|
||||
if id == value then i = idx break end
|
||||
end
|
||||
local n = #Performance.TIERS
|
||||
local nextIdx = (i - 1 + (dir or 1)) % n + 1
|
||||
return Performance.TIERS[nextIdx]
|
||||
end
|
||||
|
||||
return Performance
|
||||
@@ -241,6 +241,11 @@ function SaveData.defaultOptions()
|
||||
videoMode = "windowed",
|
||||
-- hard render frame-rate cap; render-only pacing (issue #88, FrameCap.lua)
|
||||
fpsCap = 60,
|
||||
-- graphics performance tier: auto | high | balanced | low. "auto"
|
||||
-- picks a default from the device (ARM handhelds/phones drop the heavy
|
||||
-- extras); scales TILT / GBC FX / survey ZOOM / FPS but never game
|
||||
-- logic. See src/core/Performance.lua.
|
||||
performance = "auto",
|
||||
-- Per-pipeline display levels, keyed by render_pipelines id (see
|
||||
-- src/render/Pipelines.lua). A level for a mod that is not installed
|
||||
-- is kept rather than pruned, so re-enabling the mod restores the mode
|
||||
|
||||
Reference in New Issue
Block a user