diff --git a/main.lua b/main.lua index 94824275..95d26351 100644 --- a/main.lua +++ b/main.lua @@ -1421,6 +1421,7 @@ function love.run() if love.timer then love.timer.step() end local FrameCap = require("src.core.FrameCap") + _G.POKEPORT_LOOP_PANEL_SYNC = true FrameCap.bootPanelSync() local RefreshRate = require("src.core.RefreshRate") local FixedStep = require("src.core.FixedStep") diff --git a/src/core/FrameCap.lua b/src/core/FrameCap.lua index c5feb033..1082cf85 100644 --- a/src/core/FrameCap.lua +++ b/src/core/FrameCap.lua @@ -23,7 +23,12 @@ end -- numeric FrameCap default. Android/iOS/UWP need uncapped probe isolation -- so composed GLES swapchains can lock; PortMaster handhelds likewise -- follow KMSDRM through PresentSync. +function FrameCap.loopSupportsPanelSync() + return rawget(_G, "POKEPORT_LOOP_PANEL_SYNC") == true +end + function FrameCap.prefersPanelSync() + if not FrameCap.loopSupportsPanelSync() then return false end if isHandheldEnv() then return true end if love and love.system and love.system.getOS then local osName = love.system.getOS() @@ -56,7 +61,10 @@ FrameCap.current = FrameCap.DEFAULT function FrameCap.normalize(value) value = tonumber(value) if not value then return FrameCap.DEFAULT end - if value <= 0 then return FrameCap.DISPLAY end + if value <= 0 then + if FrameCap.loopSupportsPanelSync() then return FrameCap.DISPLAY end + return FrameCap.DEFAULT + end local best, bestDiff = FrameCap.DEFAULT, math.huge for _, step in ipairs(FrameCap.STEPS) do local diff = math.abs(step - value) diff --git a/src/core/Version.lua b/src/core/Version.lua index bc4f7cc1..ae234d4d 100644 --- a/src/core/Version.lua +++ b/src/core/Version.lua @@ -7,7 +7,7 @@ local Version = { engine = "0.0.0-dev", -- game/engine release (semver). Repo default is the -- "-dev" placeholder; CI stamps the real X.Y.Z into -- the packed game.love only, never the working tree. - shell = 1, -- native-shell contract this build implements + shell = 2, -- native-shell contract this build implements payloadHost = "love", -- native host family for in-place Lua payloads. -- A payload must name the same family; this prevents -- mounting code packaged for a different native host. diff --git a/tests/engine/frame_cap_display.lua b/tests/engine/frame_cap_display.lua index 2c201952..84d9d54d 100644 --- a/tests/engine/frame_cap_display.lua +++ b/tests/engine/frame_cap_display.lua @@ -1,6 +1,7 @@ package.path = "./?.lua;./?/init.lua;" .. package.path local T = require("tests.modkit") +_G.POKEPORT_LOOP_PANEL_SYNC = true local FrameCap = require("src.core.FrameCap") local RefreshRate = require("src.core.RefreshRate") diff --git a/tests/engine/frame_cap_legacy_loop_2106.lua b/tests/engine/frame_cap_legacy_loop_2106.lua new file mode 100644 index 00000000..d06858bb --- /dev/null +++ b/tests/engine/frame_cap_legacy_loop_2106.lua @@ -0,0 +1,44 @@ +package.path = "./?.lua;./?/init.lua;" .. package.path + +local T = require("tests.modkit") +_G.POKEPORT_LOOP_PANEL_SYNC = nil +package.loaded["src.core.FrameCap"] = nil +local FrameCap = require("src.core.FrameCap") + +T.eq(FrameCap.loopSupportsPanelSync(), false, + "no marker means a run loop that predates DISPLAY pacing") +T.eq(FrameCap.normalize(0), FrameCap.DEFAULT, + "a stored DISPLAY cap paces at the default instead of 1/0") +T.eq(FrameCap.normalize(-5), FrameCap.DEFAULT, "and so does a negative cap") + +love = love or {} +love.system = love.system or {} +local savedGetOS = love.system.getOS +local savedGetenv = os.getenv +os.getenv = function(name) + if name == "POKEPORT_HANDHELD" then return "1" end + return savedGetenv(name) +end +for _, osName in ipairs({ "Android", "iOS", "UWP" }) do + love.system.getOS = function() return osName end + T.eq(FrameCap.prefersPanelSync(), false, + osName .. " does not prefer panel sync under a legacy loop") + FrameCap.current = FrameCap.DEFAULT + FrameCap.applyOptions({}) + T.check(FrameCap.current > 0, osName .. " boots with a positive cap") + FrameCap.applyOptions({ fpsCap = 0 }) + T.check(FrameCap.current > 0, osName .. " keeps a positive cap for a stored 0") + FrameCap.current = FrameCap.DEFAULT + FrameCap.bootPanelSync() + T.check(FrameCap.current > 0, "bootPanelSync stays positive on " .. osName) +end +os.getenv = savedGetenv +love.system.getOS = savedGetOS + +_G.POKEPORT_LOOP_PANEL_SYNC = true +T.eq(FrameCap.normalize(0), FrameCap.DISPLAY, + "the marker from a current love.run restores DISPLAY") +_G.POKEPORT_LOOP_PANEL_SYNC = nil +package.loaded["src.core.FrameCap"] = nil + +T.finish("frame cap legacy loop #2106") diff --git a/tests/engine/gen2_options_menu_translation_test.lua b/tests/engine/gen2_options_menu_translation_test.lua index 787dbe8e..5d003b5b 100644 --- a/tests/engine/gen2_options_menu_translation_test.lua +++ b/tests/engine/gen2_options_menu_translation_test.lua @@ -10,6 +10,7 @@ -- OPTION screen's equivalent rows), plus a vanilla no-mod case proving the -- fallback is unchanged. package.path = "./?.lua;./?/init.lua;" .. package.path +_G.POKEPORT_LOOP_PANEL_SYNC = true local T = require("tests.harness") diff --git a/tests/engine/present_sync_logic.lua b/tests/engine/present_sync_logic.lua index adf580ec..79a89754 100644 --- a/tests/engine/present_sync_logic.lua +++ b/tests/engine/present_sync_logic.lua @@ -1,6 +1,7 @@ package.path = "./?.lua;./?/init.lua;" .. package.path local T = require("tests.modkit") +_G.POKEPORT_LOOP_PANEL_SYNC = true local FrameCap = require("src.core.FrameCap") local RefreshRate = require("src.core.RefreshRate") local VSync = require("src.core.VSync")