Repository navigation
renderer: park DisplayLink while idle - #14035
Merged
Merged
Conversation
#14033 Pause the CVDisplayLink when there isn't any real work to do. Start the link after updateFrame rebuilds cells, keep it running while cell changes or animations remain pending, and resync it from the no-redraw path to sleep it again. I also did some benchmark to measure the cost of starting/stopping the display link since this includes a lot more of that and I found that a continuously running link used 29 to 35 us of CPU per callback, while starting and stopping it for every frame used 103 to 121 us. So, in some pathological case this can be worse, but its still microseconds, and in the normal case this helps Ghostty sleep a lot more. Co-authored-by: Jesse Miller <jmiller@jmiller.com>
Contributor
|
Looks good to me. The removal of syncDisplayLink at 1913 also makes sense under the drawing case, I didn't even think to check the start & stop time, now that I see it this is a good tweak. |
Contributor
Author
|
Nothing is free, not even stopping. 😄 |
mitchellh
added a commit
that referenced
this pull request
Aug 30, 2026
A follow on for #14035, we can now fix a long standing effiency/performance bug now that we park the display link while idle. This actually could cause "animated" un-focused windows to use more GPU than their focused counterparts. (AI Agent interfaces seem to love animation). 6ae1784 Unfocused surfaces stopped the CVDisplayLink and encoded a GPU frame on every PTY wakeup. A burst of close writes became that many Metal submits instead of one vsync. Keep the link running while the surface is visible and dirty or animating, whether or not it is focused. Idle surfaces still park.
danielitus
added a commit
to danielitus/ghostty
that referenced
this pull request
Sep 5, 2026
The installed app hard-froze on 2026-09-04: a render thread was stuck forever inside CVDisplayLinkStop (exposed by upstream's per-frame display-link parking, ghostty-org#14035/ghostty-org#14068), its 64-slot mailbox filled, and the app thread then blocked forever in occlusionCallback. - Keep the display link running while visible and stop it only on occlusion, and never call CVDisplayLinkStop on a link that is not running (deinit, loopExit). - Bound every app-thread push to the render mailbox (including the macOS display-id change) and the IO thread's resize push. After the first timeout the surface is marked wedged and later pushes fail fast instead of stalling the UI again. - Never lose state-carrying messages: undelivered visibility, focus, display, config and font-grid messages are kept and retried in order before the next push and on every render request, so a recovered render thread converges. Ownership of the config allocation and the font grid ref stays with the surface until delivered; the cell size is applied only once the renderer holds the new grid. - Closing a surface whose render thread is wedged detaches the thread and leaks the surface instead of joining it (embedded apprt), so a dead tab can be closed without hanging the app.
danielitus
added a commit
to danielitus/ghostty
that referenced
this pull request
Sep 26, 2026
…fixes the deadlock" This reverts commit 9b1da26. The removal assumed upstream ghostty-org#14171 (72cf508) closed the CVDisplayLinkStop deadlock. It only closes the draw_mutex variant. Follow-ups on ghostty-org#14150 (2026-09-09 through 09-25) and the open ghostty-org#14361 (2026-09-23) show the stall still happening on builds that include it: the render thread wedges inside CoreVideo during a display reconfiguration, and the app then freezes on the next unbounded mailbox push from the main thread or when surface teardown joins the thread. One reporter suspects the per-frame start/stop churn from ghostty-org#14035 widens the window. Keep the link running while visible and stop it only on occlusion, as before, until upstream lands a fix that takes CVDisplayLinkStop off the render and main threads entirely.
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.
#14033
Pause the CVDisplayLink when there isn't any real work to do.
Start the link after updateFrame rebuilds cells, keep it running while cell changes or animations remain pending, and resync it from the no-redraw path to sleep it again.
I also did some benchmark to measure the cost of starting/stopping the display link since this includes a lot more of that and I found that a continuously running link used 29 to 35 us of CPU per callback, while starting and stopping it for every frame used 103 to 121 us. So, in some pathological case this can be worse, but its still microseconds, and in the normal case this helps Ghostty sleep a lot more.