Skip to content

perf(desktop-gpui): let hidden windows and an idle target picker stop redrawing - #2594

Merged
richiemcilroy merged 6 commits into
mainfrom
perf/gpui-idle-repaints
Oct 18, 2026
Merged

richiemcilroy merged 6 commits into
mainfrom
perf/gpui-idle-repaints

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

Two idle costs in the GPUI app on macOS: hidden windows kept their display link running, and the target picker repainted every overlay on every 80 ms poll even when nothing changed.

Hidden windows (macOS 27)

platform/macos_occlusion.rs ORs NSWindowOcclusionStateVisible (0x2) into any non-zero occlusionState, which is the macOS 26 repair for gpui's display-link gate. On macOS 27 an ordered-out window reports 0x2000 rather than 0 (a plain NSWindow/NSPanel reports 0x2000 when created, 0x2002 when shown and 0x2000 after orderOut). The shim therefore told gpui every hidden window was visible, and each one kept a CVDisplayLink waking the main thread at the panel refresh rate. That includes the main window parked in the tray, the hidden helper window, and windows hidden for the picker.

// platform/macos_occlusion.rs
if raw != 0 && (!ordered_out_windows_keep_raw_state() || unsafe { is_visible(receiver) }) {
    raw | 0x2
} else {
    raw
}
  • ordered_out_windows_keep_raw_state() is kern.osproductversion major >= 27. On macOS 26 and earlier the shim behaves exactly as on main.
  • On macOS 27 an ordered-out window keeps its raw state, so gpui stops its link. Visible windows still get the bit.
  • show_native, order_front_native and show_window_without_focus re-run gpui's occlusion handler after ordering the window in (the call kick_display_link already makes), so a reshow never depends on AppKit posting an occlusion change.

Target picker (target_overlay.rs)

  • The poll now calls refresh_target_overlays only when the cursor's display, the hovered window (id and bounds), the mic meter fill or a fetched app icon changed.
  • Each overlay also observes RecordingSession and notifies when the phase changes. The device row's disabled state reads the phase and used to get updated by the constant repaint.
  • An overlay is never the active window while the picker is up, and an inactive window may not present a bare notify. So every observer an overlay holds (the picker, the main window, the session phase and the inline camera) now asks for the repaint explicitly (repaint in target_overlay.rs) instead of relying on the 80 ms repaint that used to cover them.

Re-measured on main (ada2be5ac), 2026-10-19

Isolated cap-gpui builds of main and this PR with the same profile, macOS 27, 120 Hz panel, proc_pid_rusage over 15 s after a 12 s settle, 3 interleaved rounds, medians:

State CPU %, main → PR Wakeups/s Instructions/s Footprint
Tray only 1.441 → 0.007 120 → 0 11.4 M → 0.1 M 76 → 72 MB
Display picker 5.26 → 1.83 136 → 132 45.1 M → 15.2 M 392 → 137 MB
Window picker 5.66 → 4.41 135 → 131 124 M → 97 M 390 → 154 MB
Area picker 21.8 → 22.3 161 → 168 190 M → 189 M 392 → 393 MB
Settings open 1.46 → 1.43 120 → 120 12.2 M → 11.5 M 109 → 105 MB
Camera-only picker, built-in camera 15.3 → 11.0 300 → 294 160 M → 106 M 525 → 517 MB
Display picker, built-in mic live 8.00 → 2.50 139 → 133 87.5 M → 20.9 M 397 → 138 MB
  • Screenshots taken 300 ms apart are byte-identical to main for the display, window, area and live-mic pickers. Settings matched main once settled (two early frames had one 1-pixel column off by 1/255).
  • The camera-only picker's inline preview keeps changing frame to frame on both builds, and the area picker's hint animation runs on both.
  • The area picker's cost is its animation, which this PR does not touch.

Measurements

Isolated cap-gpui perf builds, macOS 27.0, 120 Hz panel, proc_pid_rusage deltas over 15 s after a 12 s settle, 3 interleaved rounds, medians:

State CPU % Wakeups/s Instructions/s Energy Footprint
Tray only, main 0.518 120 11.8 M 8.4 mW 53 MB
Tray only, this PR 0.002 0.5 0.06 M 0.04 mW 53 MB
Settings open, main → PR 0.571 → 0.517 120 → 120 12.7 M → 12.3 M 9.9 → 8.7 mW 87.2 → 82.8 MB
Main visible / editor paused unchanged unchanged unchanged unchanged unchanged
Picker CPU % Instructions/s Energy Footprint
Display, main → PR 1.51 → 0.70 44.5 M → 15.4 M 30.8 → 12.2 mW 345 → 110 MB
Window, main → PR 2.85 → 2.04 126 M → 98 M 67 → 49 mW 368 → 133 MB
Display with live mic 3.1-3.6 → 2.2-2.6 51-53 M → 33-35 M n/a 343 → 343 MB
  • Screenshots of the reopened main window, settings, the editor, and the display/window/area overlays are identical to main (SSIM 1.000).
  • After reopening from the tray the link restarts (back to 120 wakeups/s).
  • The recording bar timer ticks and the mic meter draws, the camera preview paints, the area-picker animation runs, and the picker's mic meter keeps moving across shots 250 ms apart.
  • Memory stayed flat over 4 minutes in each state. Closing the editor returns to 66-69 MB within 2 s.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable defects were found.

Summary

This PR cuts idle drawing in hidden macOS windows and target pickers.

  • Hidden GPUI windows stop waking the main thread on macOS 27 and later.
  • The target picker redraws overlays only when their visible state changes.

Reviews (3) · Last reviewed commit: "fix(desktop-gpui): repaint target overla..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cap-status Ready Ready Preview Oct 18, 2026 4:14pm UTC

Comment thread apps/desktop-gpui/src/platform/macos_occlusion.rs Outdated
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@capy-ai capy-ai Bot 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.

Capy found 1 potential issue (1 medium).

View 1 other finding in Capy.

Open in Capy

Comment thread apps/desktop-gpui/src/target_overlay.rs

@capy-ai capy-ai Bot 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.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@capy-ai capy-ai Bot 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.

Capy found no issues.

Open in Capy

@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot 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.

Capy found no issues.

Open in Capy

@richiemcilroy
richiemcilroy merged commit 27910c6 into main Oct 18, 2026
47 checks passed

This branch was successfully deployed

2 active deployments
Preview – cap-web — 0530d88f Deployed Oct 18, 2026 by vercel[bot]
Preview – cap-status — 0530d88f Deployed Oct 18, 2026 by vercel[bot]
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.

1 participant