Skip to content

macos: fix abort on first presented frame after winit 0.30 bump - #76

Open
tenox7 wants to merge 1 commit into
techomancer:mainfrom
tenox7:fix-macos-winit030-window-handle
Open

macos: fix abort on first presented frame after winit 0.30 bump#76
tenox7 wants to merge 1 commit into
techomancer:mainfrom
tenox7:fix-macos-winit030-window-handle

Conversation

@tenox7

@tenox7 tenox7 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Problem

Since 6bbb609 (winit 0.29 → 0.30), the CLI aborts on macOS the moment the guest programs a video mode:

Rex3: Resolution changed to 1282x1024 cursor_x_adjust=5
thread 'REX3-Refresh' (3021360) panicked at src/ui.rs:99:78:
surface attributes: Unavailable
Abort trap: 6

100% reproducible: present() is gated on screen.width > 0, so the first-ever present()GlRenderer::init_gl() happens on the REX3-Refresh thread right at the first mode set. build_surface_attributes() calls window_handle(), and winit 0.30's macOS backend returns Err(HandleError::Unavailable) off the main thread (platform_impl/macos/window.rs, gated on MainThreadMarker). winit 0.29 handed out the NSView pointer from any thread, which is why this code was fine before.

Fix

Capture the RawWindowHandle once in Ui::new() on the main thread — it was already fetched there for GL context creation — stash it in GlRenderer, and build the surface attributes from it in init_gl() with window.inner_size(). glutin 0.32's CGL create_window_surface only reads the handle out of the attrs (no main-thread check of its own), so surface creation itself stays on the refresh thread, consistent with the existing GL-ownership model (unsafe impl Send for GlRenderer).

headless_gl.rs is intentionally untouched: it creates its own EventLoop on the calling thread, which already degrades gracefully (.ok()?None) off-main on macOS.

Also adds rules/macos/winit-030-window-handle-main-thread-only.md documenting the gotcha.

Testing

  • cargo build --release --features lightning,rex-jit,tlbvmap,idle-pause clean on macOS arm64 (only pre-existing warnings)
  • IRIX 5.3 boot on Apple Silicon (M1 Ultra, macOS 15.7): previously aborted ~3s in at the PROM mode set; now boots past Resolution changed to 1282x1024 with no panic

🤖 Generated with Claude Code

…hread

winit 0.30 (bumped in 6bbb609) returns HandleError::Unavailable from
window_handle() on macOS when called off the main thread; winit 0.29 handed
out the NSView pointer from any thread. GlRenderer::init_gl() runs on the
REX3-Refresh thread — lazily, from the first present(), which is gated on the
guest programming a video mode — so the CLI aborted on the first frame:

    thread 'REX3-Refresh' panicked at src/ui.rs:99:
    surface attributes: Unavailable

Capture the RawWindowHandle once in Ui::new() on the main thread (it was
already fetched there for GL context creation) and build the surface
attributes from it in init_gl() instead of calling build_surface_attributes(),
which re-queries the handle. glutin's CGL create_window_surface only reads the
handle out of the attrs, so surface creation itself stays on the refresh
thread per the existing ownership model.

headless_gl.rs keeps its .ok()? pattern: it creates its own EventLoop on the
calling thread, which already degrades gracefully off-main on macOS.
Copilot AI lite review requested due to automatic review settings August 7, 2026 07:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a macOS crash introduced by the winit 0.30 upgrade by ensuring the window handle used for GL surface creation is acquired on the main thread and then reused safely from the refresh thread, aligning with the existing “GL owned by refresh thread” model in IRIS.

Changes:

  • Cache RawWindowHandle in Ui::new() (main thread) and store it in GlRenderer.
  • Build GL surface attributes in GlRenderer::init_gl() using the cached handle plus the current window.inner_size(), avoiding window_handle() calls off the main thread on macOS.
  • Add a rules note documenting the winit 0.30 macOS main-thread-only handle constraint and the established workaround.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/ui.rs Caches RawWindowHandle on the main thread and uses it to create the window surface on the refresh thread, preventing the macOS abort after the winit 0.30 bump.
rules/macos/winit-030-window-handle-main-thread-only.md Documents the winit 0.30 macOS behavior change and the required pattern for handle capture/surface creation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@danifunker

Copy link
Copy Markdown
Contributor

Looks good to me, thanks for this. I must have missed it during my testing!

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.

3 participants