fix(scroll): recover correct refresh rate after sleep/wake - #1022
Open
ismailcolakk13 wants to merge 1 commit into
Open
fix(scroll): recover correct refresh rate after sleep/wake#1022ismailcolakk13 wants to merge 1 commit into
ismailcolakk13 wants to merge 1 commit into
Conversation
CVDisplayLink was created during the display's post-wake transition window, where CGDisplayCopyDisplayMode and CVDisplayLink both report the same temporary Hz (e.g. 60 Hz on a 120 Hz panel). The existing ratio check (link < display × 0.7) passed trivially, so the verify loop exited early and left smooth scrolling stuck at the wrong rate until the user manually toggled the refresh rate setting. Three layered fixes: 1. Track lastLinkHz at create() time. verifyRateAndFixIfNeeded now also triggers a rebuild when the display Hz has risen more than 5% since the link was created, catching the case where both sources agreed on the transitional value. 2. Add a wake-specific verify sequence (1 s, 3 s, 6 s, 12 s) via scheduleRateVerify(isWake:), longer than the normal [2, 4, 8] s window, to cover the full display stabilisation period. 3. Register NSWorkspace.didWakeNotification inside startKeeper / stopKeeper (matching existing lifecycle pattern) to kick off the wake verify sequence 0.5 s after wake, before the first scroll event arrives. AppDelegate gains a screenChangeLateTimer that fires 6 s after didChangeScreenParametersNotification as a final safety net: the existing 1 s debounce can still land inside the transition window, while 6 s reliably clears both the window and the 3 s recreate cooldown. Co-Authored-By: Antigravity <noreply@google.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(scroll): recover correct refresh rate after sleep/wake (late VBL)
Motivation
After waking from sleep, smooth scrolling runs at a low frame rate (e.g. 60 Hz) on a high-refresh display. The only workaround is manually switching the display refresh rate in System Settings and switching back — that forces a fresh
CVDisplayLinkat the correct rate.The WindowServer message
Bad CurrentVBLDelta for display N is zero, defaulting to 60Hzis visible in the system console immediately after wake. This is the root cause: the display's Vertical Blank (VBL) signal has not been re-established yet, so the OS defaults to 60 Hz. At that momentCVDisplayLinkis created with the wrong rate, and the existing rate-verify mechanism (link Hz < display Hz × 0.7) fails silently — becauseCGDisplayCopyDisplayModealso returns 60 Hz in this transitional state, so both sides of the ratio agree and the check exits early.What changed
Mos/ScrollCore/ScrollPoster.swiftlastLinkHz— records theCVDisplayLinknominal Hz at creation time.verifyRateAndFixIfNeededgains a second trigger condition: if the display Hz has risen more than 5% since the link was created (nominal > lastLinkHz × 1.05), the link is rebuilt. This catches the VBL-late case where both sources agreed on the wrong transitional value.wakeRateVerifyDelays: [1.0, 3.0, 6.0, 12.0]— a longer verify sequence for the post-wake window, replacing the normal[2, 4, 8]s delays.scheduleRateVerify(isWake:)— routes to the wake or normal delay array.startKeeper/stopKeeperregister and deregister aNSWorkspace.didWakeNotificationobserver (matching the existing lifecycle pattern) that triggersscheduleRateVerify(isWake: true)0.5 s after wake.Mos/AppDelegate.swiftscreenChangeLateTimer— fires a secondrecreateDisplayLink()call 6 s afterdidChangeScreenParametersNotification. The existing 1 s debounce can still land inside the VBL transition window; 6 s reliably clears both the window and the 3 srecreateCooldown.Validation
Possible risks
NSWorkspacenotification listener, tied to the samestartKeeper/stopKeeperlifecycle as the existingkeepertimer — deregistered cleanly ondisable().rateRoseAfterCreatecondition uses a 5% tolerance to avoid spurious rebuilds from measurement jitter; normal steady-state operation won't trigger it.Related
Extends the existing rate-verify mechanism introduced for issue #958. Same pattern, additional trigger condition and a dedicated wake path.