Skip to content

fix: pump AppKit sooner and install default menus after the first frames - #608

Merged
Ernxst merged 2 commits into
mainfrom
fix/600-first-frame
Sep 21, 2026
Merged

Ernxst merged 2 commits into
mainfrom
fix/600-first-frame

Conversation

@Ernxst

@Ernxst Ernxst commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Harness: Claude Code
Agent: none
Model: anthropic/claude-opus-5

Task statements
  1. Compare this fork with a public benchmark of one Mac app built in 21 frameworks, and bring startup closer to plain GPUI. (Working-directory and workflow instructions omitted.)

Part of #600.

Two of #600's three startup changes on macOS:

  • Pump AppKit straight after init. The frame loop's first idle tick ran after frameMs (8 ms). That tick runs AppKit's post-show work, which ends in the window being reported visible and the display link starting, so it now runs immediately. React's first commit already happens before startFrameLoop. Windows and Linux use the timer path, which is unchanged.
  • Install the default menus after the second frame. Default menus load WritingToolsUI and with it SwiftUI, WebKit and about 100 other frameworks (11.5 ms). With menus unset they are now installed from a second nested on_next_frame. Caller-supplied menus are still installed during init, so an invalid spec still fails init().

GPUIX hello-world, launch to second frame, bun scripts/app-bench.ts --runs 5 alternating between main and this branch, 3 rounds, with other builds running on the machine:

Round main median This PR median
1 703 ms 684 ms
2 635 ms 637 ms
3 698 ms 671 ms
Median of rounds 698 ms 671 ms

The absolute figures are inflated by the load; the difference is about 27 ms. examples/counter.tsx shows its content on the first screenshot and gets the default App and Window menus; examples/menus.tsx still shows its own menus.

The third change, committing React before the window's first present, is not in this PR. Committing before init() fails because apply_batch invalidates a window that does not exist yet, and useWindowSize would read an 800×600 fallback.

Risk: the default menus appear two frames after the window instead of before it, about 30 ms at 60 Hz, so a menu shortcut pressed in that window does nothing. Cmd+Q was not exercised by a synthesised keystroke here, because macOS blocks keystroke automation without an Accessibility grant.

🤖 Generated with Claude Code

Ernxst and others added 2 commits September 22, 2026 00:33
The first frame-loop tick waited `frameMs` (8ms) before pumping AppKit,
delaying the post-show occlusion pump that reports the window visible.
Run the first idle tick synchronously instead of scheduling it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The default menu bar (no `menus` option) loads WritingToolsUI and, with
it, SwiftUI, WebKit and about a hundred other frameworks, all before the
window's first present. Defer that install until after the window's
second frame; a caller-supplied menu spec still installs synchronously
during init so an invalid spec still fails `init()` immediately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Ernxst
Ernxst merged commit e7bac73 into main Sep 21, 2026
4 checks passed
@Ernxst
Ernxst deleted the fix/600-first-frame branch September 23, 2026 15:40
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