Skip to content

Fix macOS wgpu live resize with low-latency surfaces - #8229

Merged
lucasmerlin merged 2 commits into
emilk:mainfrom
yay:codex/macos-wgpu-live-resize
Jun 25, 2026
Merged

Fix macOS wgpu live resize with low-latency surfaces#8229
lucasmerlin merged 2 commits into
emilk:mainfrom
yay:codex/macos-wgpu-live-resize

Conversation

@yay

@yay yay commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Summary

This fixes macOS live-resize behavior for the eframe/egui-wgpu path when using the low-latency wgpu surface configuration.

The problem I was seeing is that native window resize can look visibly below the baseline expected from a desktop GUI: stale or stretched frames (manifesting as wobble/jitter), or severe lag while dragging a window edge.

The fix has three parts:

  • use CAMetalLayer.presentsWithTransaction during live resize to avoid stale/stretched frames
  • temporarily use at least desired_maximum_frame_latency = 2 while live resize is active, so transaction presentation does not stall when the app normally uses SurfaceConfig::LOW_LATENCY
  • treat macOS WindowEvent::Moved as part of the live-resize event stream, since resizing from the top or left edge changes the window origin

This PR depends on the winit-side AppKit live-resize timing fix in rust-windowing/winit#4588

A renderer-only frame-latency change is not enough by itself. The temporary latency bump only solves the drawable starvation caused by combining presentsWithTransaction with SurfaceConfig::LOW_LATENCY. It does not change when winit emits resize/redraw events, whether redraws are delivered during AppKit's live-resize event-tracking loop, or whether the surface size is derived from the current backing rect.

That is why the winit fix is needed first: it makes the windowing layer report the current AppKit backing size and request redraws from the live-resize/display callbacks. egui-wgpu still needs this PR on top because winit does not own the wgpu Surface or the underlying CAMetalLayer presentation policy.

In other words: winit fixes when the windowing layer reports resize/redraw work, while this PR fixes how egui-wgpu presents Metal-backed wgpu frames during that resize.

Why change the existing feature?

The existing macos-window-resize-jitter-fix feature addresses one symptom by enabling transaction presentation during resize, but it is not enough for the low-latency wgpu path.

In particular, presentsWithTransaction and SurfaceConfig::LOW_LATENCY interact poorly during AppKit live resize. The old code avoids that by skipping transaction presentation when latency is 1, but that means low-latency users get the resize jitter/wobble back.

This PR keeps the low-latency path normally, but temporarily bumps frame latency only while live resize is active. That gives the resize path enough drawable slack without changing normal interaction latency.

I removed the macos-window-resize-jitter-fix feature because this seems like the behavior the macOS wgpu path should have by default, not a separate opt-in. If keeping the feature as a no-op compatibility alias is preferred, I can adjust the PR.

Validation

I created a small demo app that somewhat resembles the layout of my actual app and highlights both horizontal and vertical resize jitter:

  • a borderless macOS window
  • a simple toolbar
  • a scrolling side list
  • SurfaceConfig::LOW_LATENCY

The toolbar and list make stale or stretched frames easy to see during native resize. The jitter is visible even on the traffic light buttons.

Recordings:

Before 1: no transaction presentation, low latency

Shows jitter/wobble and stale/stretched frames during live resize.

before-1-jitter.mp4

Before 2: transaction presentation with low latency

Shows the other failure mode: live resize can become severely laggy when
transaction presentation is used while keeping SurfaceConfig::LOW_LATENCY.

before-2-lag.mp4

After: patched egui-wgpu + patched winit, low latency

No visible wobble/jitter and no severe live-resize lag.

after-fixed.mp4

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Preview available at https://egui-pr-preview.github.io/pr/8229-codexmacos-wgpu-live-resize
Note that it might take a couple seconds for the update to show up after the preview_build workflow has completed.

View snapshot changes at kitdiff

@yay
yay marked this pull request as ready for review June 7, 2026 10:33
@yay
yay requested a review from Wumpf as a code owner June 7, 2026 10:33
@yay

yay commented Jun 7, 2026

Copy link
Copy Markdown
Contributor Author

Just submitted another winit PR that fixes another related resize issue: rust-windowing/winit#4589

It's not blocking this PR but something we'd probably want to wait for to be merged as well, so next egui release depends on winit version that fixes both issues.

@emilk

emilk commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR!

This PR depends on the winit-side AppKit live-resize timing fix in rust-windowing/winit#4588

Should we still try to merge this egui PR now, or wait until there is a new winit release with your fix?

@yay

yay commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@emilk Thanks for catching this. My dependency statement was incorrect. I validated both patches together and thought that the winit change was required.

I just tested them independently and my egui app still resizes smoothly with SurfaceConfig::LOW_LATENCY.
eframe already repaints synchronously through RepaintNow when it receives WindowEvent::Resized, so Metal transaction handling and temporary frame-latency increase in this PR are enough.

I also retested the standalone winit/Metal demo in rust-windowing/winit#4588 which renders only from RedrawRequested. It does jitter without winit patch. So it's still useful but not required for this PR to be merged.

@yay

yay commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Also heads-up: I still see occasional choppiness when increasing the window size in just released macOS 27 Developer Beta with this patch in. I haven't tested this on macOS 26 because I'm still on Sequoia.

@emilk emilk added this to the 0.35.0 milestone Jun 10, 2026
@yay
yay force-pushed the codex/macos-wgpu-live-resize branch 2 times, most recently from f480af1 to f84b6c7 Compare June 13, 2026 13:22
@yay
yay force-pushed the codex/macos-wgpu-live-resize branch from 5ad0811 to ca5f15a Compare June 20, 2026 21:04
@lucasmerlin lucasmerlin modified the milestones: 0.35.0, 0.36.0 Jun 24, 2026
@lucasmerlin

Copy link
Copy Markdown
Collaborator

What is is_live_resizing?
It does not seem to exist, not in winit main, not on docs.rs and not on your winit PR?

@emilk emilk added the blocked Can't make progress right now label Jun 24, 2026
@yay
yay force-pushed the codex/macos-wgpu-live-resize branch from f808fec to 6cee421 Compare June 25, 2026 10:57
@yay

yay commented Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

@lucasmerlin Ah, thanks for catching this! The PR branch accidentally picked up my later local experiment that depended on WindowExtMacOS::is_live_resizing API in my winit fork.

That was not meant to be part of this PR. I just cleaned it up and retested to confirm the fix still works.
The demo I used to verify is here: demo.zip
And custom_window_frame example in egui itself shows no signs of jitter either.

@yay
yay force-pushed the codex/macos-wgpu-live-resize branch from 6cee421 to 866367d Compare June 25, 2026 11:33

@lucasmerlin lucasmerlin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Awesome, butter smooth now with no streaching or lag!

Comment thread crates/egui-wgpu/Cargo.toml Outdated
Comment on lines +71 to +72
[target.'cfg(target_os = "macos")'.dependencies]
wgpu = { workspace = true, features = ["metal"] }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It'd be great if we could keep metal optional, since some people might be using egui with vulkan or gl on macos.
I guess that was the original reason the feature flag was added.

} else {
desired_maximum_frame_latency
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice, smart way to go about that!

@lucasmerlin lucasmerlin added bug Something is broken eframe Relates to epi and eframe and removed blocked Can't make progress right now labels Jun 25, 2026
@lucasmerlin
lucasmerlin enabled auto-merge (squash) June 25, 2026 13:52
@lucasmerlin
lucasmerlin merged commit a8d09eb into emilk:main Jun 25, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is broken eframe Relates to epi and eframe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants