Skip to content

fix(scroll): re-verify display link refresh rate after rebuild (#958) - #991

Open
wiskiiii wants to merge 1 commit into
Caldis:masterfrom
wiskiiii:fix/scroll-display-link-rate-958
Open

fix(scroll): re-verify display link refresh rate after rebuild (#958)#991
wiskiiii wants to merge 1 commit into
Caldis:masterfrom
wiskiiii:fix/scroll-display-link-rate-958

Conversation

@wiskiiii

Copy link
Copy Markdown

Problem and cause analysis

Refer to #958 (comment)

Fix

After each CVDisplayLink creation, schedule a few delayed re-checks (+2s, +4s, +8s). If the link's nominal rate is well below the display's actual rate (< 70%), rebuild and clear the cooldown so any pending screenChange can also rebind.

  • Only touches ScrollPoster.swift (+79 lines, no deletions)
  • No changes to scroll/input event handling, cooldown logic, or other files
  • Self-limiting: stops checking as soon as the rate matches, max 3 attempts

Testing

Ran a debug build with rate-verify logging on my machine (MBP + 144Hz external display, macOS 26.5) for a week. The fix fired and self-corrected quite a few times, each time the link was rebound from 60Hz to 144Hz within ~2 seconds, with no manual restart needed. Prior to the fix, the same scenario required an app restart every time.

Impact

Scroll smoothing behavior is unchanged — this only affects when and whether the CVDisplayLink gets recreated after a display transition. Worst case is an extra harmless recreation or two after a display wake.

…s#958)

After display sleep/wake or reconnect, CVDisplayLinkCreateWithActiveCGDisplays
can bind to a transitional low refresh rate (e.g. 60Hz while the display is
still settling back to 144Hz), leaving smooth scrolling stuck at that low rate
until the app is restarted. The existing recovery only catches a fully stalled
link, not one that is alive but bound to the wrong rate.

Schedule a few delayed re-checks after each link creation; if the link's
nominal rate is well below the active display's actual rate, rebuild to catch
up and clear the recreate cooldown so a following screen-change can rebind
against the settled configuration.
Copilot AI review requested due to automatic review settings July 18, 2026 16:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #958 by adding a self-healing mechanism in ScrollPoster to re-verify CVDisplayLink’s nominal refresh rate after (re)creation and recreate the link if it is clearly bound to a transitional/too-low refresh rate after display wake/reconnect.

Changes:

  • Adds delayed refresh-rate verification attempts (+2s, +4s, +8s) after each successful CVDisplayLink creation.
  • If the display link nominal Hz is < 70% of the maximum active display Hz, rebuilds the display link and clears the recreate cooldown to allow subsequent screen-change rebinding.
  • Adds helper functions to read the display link nominal refresh rate and the max active display refresh rate.

Comment on lines +336 to +340
guard rateVerifyAttemptsLeft > 0 else { rateVerifyTimer = nil; return }
let delay = rateVerifyDelays[rateVerifyDelays.count - rateVerifyAttemptsLeft]
rateVerifyTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self] _ in
self?.verifyRateAndFixIfNeeded()
}
Comment on lines +360 to +362
if wasRunning, let refreshed = poster {
CVDisplayLinkStart(refreshed)
}
Comment on lines +342 to +348
// 复查: link 标称率明显低于显示器实际率 -> 重建追平 (幂等, 不受 recreateCooldown 限制)
private func verifyRateAndFixIfNeeded() {
rateVerifyAttemptsLeft -= 1
guard let current = poster else { rateVerifyTimer = nil; return }
let nominal = linkNominalHz(current)
let maxHz = maxActiveDisplayHz()
guard nominal > 1, maxHz > 1, nominal < maxHz * rateVerifyRatio else {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants