renderer: park DisplayLink while idle (#14035)

#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.
This commit is contained in:
Mitchell Hashimoto
2026-08-26 11:35:35 -07:00
committed by GitHub

View File

@@ -1191,11 +1191,18 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
};
}
// If we're not visible, then we want to stop the display link
// because it is a waste of resources and we can move to pure
// change-driven updates.
if (self.visible and self.focused) {
display_link.start() catch {};
const should_run =
// Non-visible windows never vsync
self.visible and
// Non-focused windows only render on-demand
self.focused and
// Only vsync if we have cell changes or animation
(self.cells_rebuilt or self.animationWake() != null);
if (should_run) {
if (!display_link.isRunning()) {
display_link.start() catch {};
}
} else {
display_link.stop() catch {};
}
@@ -1593,6 +1600,9 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Update custom shader uniforms that depend on terminal state.
self.updateCustomShaderUniformsFromState();
}
// Start the display link now that the rebuilt frame is ready.
self.syncDisplayLink(null, null);
}
/// Draw the frame to the screen.
@@ -1670,6 +1680,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// apprt may be swapping buffers and display an outdated frame
// if we don't draw something new.
try self.api.presentLastTarget();
// Resync the display link because we can probably pause
// the display link at this point.
self.syncDisplayLink(null, null);
return;
}
self.cells_rebuilt = false;