mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
f82f565cae
the display sync stuff i added was probing whether vsync was actually working, but it was measuring the wrong thing. during the probe we software-cap at 60 for safety, and the probe was looking at the gap between frames... which includes the sleep 😅 ..... so it always looked like sync was fine even when the driver was ignoring it. on something like the ally x on windows thats a real problem. vsync says on, probe says gated, we lift the cap and snap logic to the panel hz, then youre basically uncapped. at 2–4x that turns into hitching, dropped frames, dropped input, that weird half second freeze. speed swapping wasnt desyncing the driver, it was just making the bad path hurt more.
the solution is just we time present() itself now, not the gap after it, this way the warmup sleep cant fake a pass. if sync is unclear or broken we just stay on a capped 60 and dont snap logic. Also fixed fixedstep so it snaps wall clock dt before applying speed, so in general the 2-4x speed swaps dont screw the pacing math anymore
101 lines
3.7 KiB
Lua
101 lines
3.7 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.modkit")
|
|
local FrameCap = require("src.core.FrameCap")
|
|
local RefreshRate = require("src.core.RefreshRate")
|
|
local VSync = require("src.core.VSync")
|
|
local PresentSync = require("src.core.PresentSync")
|
|
local FixedStep = require("src.core.FixedStep")
|
|
local PresentProbe = require("src.core.PresentProbe")
|
|
|
|
RefreshRate.reset()
|
|
VSync.reset()
|
|
PresentSync.reset()
|
|
PresentProbe.reset()
|
|
|
|
local function measure(hz)
|
|
RefreshRate.reset()
|
|
for _ = 1, 31 do RefreshRate.sample(1 / hz) end
|
|
end
|
|
|
|
measure(144)
|
|
T.eq(RefreshRate.mismatch(), 144, "144Hz is not a multiple of 60")
|
|
|
|
FrameCap.apply(FrameCap.DISPLAY)
|
|
VSync.reset()
|
|
love.window.getVSync = function() return 0 end
|
|
love.window.setVSync = function() end
|
|
VSync.apply("off")
|
|
T.eq(PresentSync.logicRefreshPeriod(), nil,
|
|
"DISPLAY with vsync off does not snap logic to the panel (#1958)")
|
|
|
|
VSync.reset()
|
|
love.window.getVSync = function() return 1 end
|
|
VSync.apply("on")
|
|
PresentProbe._testSetState({ osLinux = false, ready = true, gated = false,
|
|
needsSoftwareCap = true, nest = "windows" })
|
|
T.eq(PresentSync.logicRefreshPeriod(), nil,
|
|
"DISPLAY with broken sync falls back to software cap, not panel snap")
|
|
|
|
PresentProbe._testSetState({ osLinux = false, ready = true, gated = true,
|
|
needsSoftwareCap = false, nest = "windows" })
|
|
T.eq(PresentSync.logicRefreshPeriod(), 1 / 144,
|
|
"DISPLAY with working sync tracks the panel")
|
|
|
|
FrameCap.apply(60)
|
|
T.eq(PresentSync.logicRefreshPeriod(), nil,
|
|
"a 60 cap on a 144Hz panel leaves refresh snapping off")
|
|
|
|
FrameCap.apply(144)
|
|
T.eq(PresentSync.logicRefreshPeriod(), 1 / 144,
|
|
"a 144 cap on a 144Hz panel may snap logic to the panel")
|
|
|
|
PresentSync.applyFixedStepPeriod()
|
|
T.eq(FixedStep.refreshPeriod, 1 / 144, "applyFixedStepPeriod writes the module field")
|
|
|
|
FrameCap.apply(FrameCap.DISPLAY)
|
|
VSync.apply("on")
|
|
PresentProbe._testSetState({ osLinux = false, ready = true, clearGated = true,
|
|
needsSoftwareCap = false, nest = "windows" })
|
|
T.check(PresentSync.probingDisplaySync(), "DISPLAY+vsync probes before a verdict")
|
|
T.check(PresentSync.needsSoftwareCap(),
|
|
"warmup still uses FrameCap as a thermal net during the probe")
|
|
T.eq(PresentSync.logicRefreshPeriod(), nil, "and does not snap logic during warmup")
|
|
|
|
PresentProbe._testSetState({ gated = true, needsSoftwareCap = false })
|
|
T.check(not PresentSync.probingDisplaySync(), "a finished probe clears warmup")
|
|
T.check(not PresentSync.needsSoftwareCap(), "so software cap stops once sync is confirmed")
|
|
|
|
-- Broken sync after an honest ungated probe keeps the thermal net and no snap.
|
|
PresentProbe._testSetState({ gated = false, needsSoftwareCap = true })
|
|
T.check(PresentSync.needsSoftwareCap(), "ungated DISPLAY keeps FrameCap")
|
|
T.eq(PresentSync.logicRefreshPeriod(), nil, "and never snaps logic to the panel")
|
|
|
|
VSync.apply("on")
|
|
PresentProbe._testSetState({ needsSoftwareCap = true, gated = false })
|
|
T.check(PresentSync.vsyncEnableBlocked(), "broken sync blocks enabling vsync")
|
|
T.check(PresentSync.vsyncStepAllowed("on", 1), "but one step to OFF is allowed")
|
|
T.check(not PresentSync.vsyncStepAllowed("on", -1),
|
|
"while a step that stays on/adaptive is not")
|
|
|
|
-- FixedStep snaps wall-clock dt, then applies speed (not the reverse).
|
|
FixedStep.refreshPeriod = 1 / 60
|
|
local steps = 0
|
|
FixedStep:init(function() steps = steps + 1 end)
|
|
FixedStep.maxAccum = 0.25
|
|
FixedStep:update(1 / 60, 4)
|
|
T.eq(steps, 4, "4X on a snapped 60Hz frame runs four logic steps")
|
|
steps = 0
|
|
FixedStep:update(1 / 60, 2)
|
|
T.eq(steps, 2, "and 2X runs two")
|
|
FixedStep.refreshPeriod = nil
|
|
|
|
love.window.getVSync, love.window.setVSync = nil, nil
|
|
VSync.reset()
|
|
PresentSync.reset()
|
|
PresentProbe.reset()
|
|
RefreshRate.reset()
|
|
FrameCap.apply(FrameCap.DEFAULT)
|
|
|
|
T.finish("present sync logic")
|