From 3095bcb76025f5558e3e6c35de682b5001d2ff7d Mon Sep 17 00:00:00 2001 From: fireblue Date: Mon, 1 Jun 2026 11:42:22 +0800 Subject: [PATCH] fix(desktop): stop pinning macOS displays at max refresh rate (#1242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macOS compositor watchdog called webContents.setBackgroundThrottling(false) unconditionally for the window's whole lifetime. That disables Chromium's idle frame-rate throttling, so the GPU process's CADisplayLink never idles — pinning ProMotion panels at their maximum refresh rate (120Hz) continuously, even while the app is idle or occluded, which drains the battery. The call was never needed: the probe already skips non-producing windows via its screen-lock / isVisible / isMinimized / document.visibilityState guards, and the freeze it targets happens while the window is visible and focused, where background throttling never applies. Removing it lets the compositor idle and the display drop to a low adaptive rate when nothing animates; freeze detection is unaffected. A comment and a docs note keep it from being re-added. Co-authored-by: Claude Opus 4.8 (1M context) --- docs/development.md | 6 ++++++ .../desktop/src/window/compositor-watchdog/index.ts | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/development.md b/docs/development.md index 03d8d5f5d..c76e87b61 100644 --- a/docs/development.md +++ b/docs/development.md @@ -123,6 +123,12 @@ GPU process so Chromium rebuilds the display link. The probe is skipped while the screen is locked or the window is hidden or minimized, since a window legitimately stops producing frames then. +The watchdog deliberately leaves background throttling **enabled**. Calling +`webContents.setBackgroundThrottling(false)` would keep the compositor producing +frames non-stop, pinning ProMotion displays at 120Hz forever and draining the +battery while the app is idle — so do not re-add it. The probe's visibility +guards already prevent throttling from causing a false stall. + ### Daemon logs Check `$PASEO_HOME/daemon.log` for daemon logs. The default level is `info`; set diff --git a/packages/desktop/src/window/compositor-watchdog/index.ts b/packages/desktop/src/window/compositor-watchdog/index.ts index 654abde39..accb249c4 100644 --- a/packages/desktop/src/window/compositor-watchdog/index.ts +++ b/packages/desktop/src/window/compositor-watchdog/index.ts @@ -67,8 +67,15 @@ export function setupDarwinCompositorWatchdog(win: BrowserWindow): void { return; } - // Keep producing frames while occluded so the probe is not fooled by throttling. - win.webContents.setBackgroundThrottling(false); + // Deliberately do NOT call win.webContents.setBackgroundThrottling(false) here. + // Disabling background throttling keeps Chromium's compositor producing frames + // continuously, which pins ProMotion displays at their max refresh rate (120Hz) + // forever and drains the battery even while the app sits idle. The probe does + // not need it: the visibility guards below (screen lock / isVisible / + // isMinimized / document.visibilityState) already skip windows that legitimately + // stop producing frames, so throttling cannot fool the probe into a false stall. + // The freeze this watchdog targets happens while the window is visible and + // focused (just after display wake), where background throttling never applies. let stalledChecks = 0; let recovering = false;