The sbc/portmaster launcher exports POKEPORT_AUDIO_RATE=22050 and
docs/linux-arm-sbc.md describes it as halving synthesis CPU on
Cortex-A53 handhelds, but nothing in the engine read it: ChipSynth
always rendered at 44100 Hz, and snapTicks baked that rate in as the
integer rational 1470/512 (so once the rate does change, music plays
at half tempo/pitch unless snapTicks follows).
Read the env var at module load (validated to 8000-48000, 44100
fallback) and make snapTicks rate-aware. At 44100 the new form is
arithmetically identical to the old one
((ticks*44100 + 7680)/15360 == (ticks*1470 + 256)/512), so desktop
behavior is unchanged.
Tested on an RG351MP (RK3326, dArkOS): correct music tempo and pitch
at 22050 Hz, halved synthesis work on the audio worker.
On weak single-core handhelds (RK3326/RG351MP) the main thread sits at
100% CPU, so every millisecond of jitter drops a frame: constant
stuttering while walking even at PERFORMANCE LOW with all mods off,
while average CPU usage stays unchanged. Three sources of per-frame
garbage and pause on the hot path:
- Game:step / Game:logicSpeed passed inline closures to ModRuntime.call,
allocating a fresh function 60 times per second. Hoist both to
module-level locals; behavior is identical.
- Game:update advanced the incremental collector every rendered frame.
Space it to every 4th frame: the explicit frees the comment refers
to still do the heavy lifting, collection stays incremental, and the
stepping itself stops competing with the 16.6ms frame budget.
- checkEmergencyQuit called love.joystick.getJoysticks() (a fresh
table) twice per frame to guard a 5-second hold combo. Cache the
joystick list and refresh it once per second; a 1s hotplug delay is
irrelevant against a 5s hold.
Tested on an RG351MP (RK3326, dArkOS, v0.2.41): eliminates the
constant walking stutter with all mods off, and with 11 mods
re-enabled. Average CPU is unchanged (the cost was variance, not
load), which matches the diagnosis.
- Updated PresentProbe to measure inter-present cadence accurately, ensuring that ambiguous signals default to FrameCap.
- Refined classification logic to prioritize stable cadence over unreliable hardware gating, enhancing the robustness of frame pacing.
- Adjusted PresentSync documentation to clarify probe isolation and its implications for sync confirmation.
- Improved test cases to validate the new cadence-based gating logic across various platforms, ensuring consistent behavior in frame pacing.
- Updated PresentSync to accurately measure present() block time, ensuring that the probe does not misclassify sync status due to FrameCap sleep artifacts.
- Enhanced FixedStep to implement a hard ceiling on catch-up debt, preventing input starvation during high-speed scenarios.
- Adjusted logic in Game and Game2 to utilize the new catchupLimit function for setting maxAccum, ensuring consistent behavior across speed multipliers.
- Improved test coverage for PresentProbe and PresentSync to validate the new logic and edge cases.
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
- Introduced PresentSync module to manage display synchronization.
- Updated love.run() to handle display changes and resizing events with PresentSync.
- Enhanced FrameCap logic to accommodate PresentSync requirements.
- Modified VSync to report effective states and handle driver quirks.
- Updated options menus to reflect PresentSync availability and restrictions.
- Added tests to validate PresentSync functionality and its interaction with VSync settings.
should address issue #1910 and issue #1958