Skip to content

Fix AppKit live resize redraw timing - #4588

Merged
tronical merged 1 commit into
rust-windowing:masterfrom
yay:steady/macos-live-resize
Jun 24, 2026
Merged

Fix AppKit live resize redraw timing#4588
tronical merged 1 commit into
rust-windowing:masterfrom
yay:steady/macos-live-resize

Conversation

@yay

@yay yay commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

This fixes macOS live-resize behavior for layer-backed winit views.

While investigating resize artifacts in a downstream app and looking at other AppKit/Metal applications with smooth live resize behavior such as Zed editor, I found that AppKit can update and ask a layer-backed content view to display during live resize through paths that winit was not fully using for surface resize/redraw timing.

The main issue is that during live resize, the view backing size and redraw timing need to stay synchronized with AppKit's resize/display callbacks. Otherwise surface users can render with stale size information or miss redraw opportunities during the event-tracking run loop. In practice this shows up as visible wobble/jitter and stale or stretched frames while the window edge or corner is being dragged.

This PR changes the AppKit view path to:

  • set NSViewLayerContentsRedrawDuringViewResize for the content view
  • handle setFrameSize: by updating the surface size and requesting redraw during live resize
  • handle viewDidChangeBackingProperties for backing-scale/backing-size changes
  • handle displayLayer: as a redraw signal during live resize
  • report surface size from convertRectToBacking(bounds) instead of deriving it from the window/frame size

Validation

I used a small winit + CAMetalLayer demo that:

  • renders an animated high-contrast grid
  • paints only from WindowEvent::RedrawRequested
  • does not use an external AppKit timer
  • does not enable CAMetalLayer.presentsWithTransaction

I recorded the same demo before and after the fix:

Before:

before-the-fix.mp4

After:

after-the-fix.mp4

In the before video, live resize shows stale/stretched frames. In the after video, resizing stays synchronized and smooth.

I also validated the same change against winit 0.30.13, since that is the version used by the downstream app where I first reproduced the issue.


Downstream validation/follow-up in egui: emilk/egui#8229.

That PR uses this winit change as the windowing-layer part of the fix, then handles the renderer-specific egui-wgpu/Metal presentation policy separately.

@yay

yay commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

@madsmtm Gentle bump - does this approach look acceptable?

@yay
yay force-pushed the steady/macos-live-resize branch 2 times, most recently from e142221 to 3508b03 Compare June 13, 2026 18:03
@kchibisov

Copy link
Copy Markdown
Member

@tronical @ogoffart would you mind taking a look? I don't have macOS machine around to check macOS stuff myself, but this might be interesting to you?

Comment thread winit-appkit/src/view.rs Outdated
Comment thread winit-appkit/src/view.rs Outdated
Comment thread winit-appkit/src/view.rs Outdated
Comment thread winit-appkit/src/view.rs Outdated

@tronical tronical 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.

Looks overall good to me. A few questions inside, and I think that unsafe call can be replaced with a clean safe call :)

@yay
yay force-pushed the steady/macos-live-resize branch 2 times, most recently from aeb21f1 to 2bafd96 Compare June 24, 2026 12:23
@yay
yay force-pushed the steady/macos-live-resize branch from 2bafd96 to 021fb7c Compare June 24, 2026 13:23
@yay

yay commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for reviewing! Just addressed all comments. Sorry it took me a while to get back to it, even though I was the one who bumped it.

@tronical

Copy link
Copy Markdown
Contributor

This only touches appkit files and that part is green in the CI. the linux failures are unrelated and already fixed on master. So merging this one :)

@tronical
tronical merged commit 27e17e3 into rust-windowing:master Jun 24, 2026
44 of 50 checks passed
lucasmerlin added a commit to emilk/egui that referenced this pull request Jun 25, 2026
## 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](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`](https://github.com/emilk/egui/blob/71c4ff3c337a08bee934f249463d6701bf76b420/crates/egui-wgpu/src/winit.rs#L417),
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.


https://github.com/user-attachments/assets/2cf4467b-e14c-4f41-8021-0b8c23f41004

### 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`.


https://github.com/user-attachments/assets/2f866790-f472-4ede-a3c0-480e8f0f041a

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

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


https://github.com/user-attachments/assets/59e46e9f-7906-4b5c-a6c7-1d09eae644cd

---------

Co-authored-by: lucasmerlin <hi@lucasmerlin.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants