-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Fix jittering during window resize on MacOS for WGPU/Metal #7641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…rame integration.
|
Preview available at https://egui-pr-preview.github.io/pr/7641-osxmetalresizejitterfix View snapshot changes at kitdiff |
|
@Wumpf would love to hear your feedback on this =) |
|
thanks for setting this up! will have a more thorough look and think in the coming days
You should be able to poke the wgpu adapter/device to ask it at runtime whether it's a metal device 🤔 Edit: Ah sorry, you already have that check via cast. The problem is that you don't know whether the types are available. That's nasty 🤔 |
|
This looks very promising! Thanks for working on this ❤️ |
|
Thanks for investigating this deeper and coming up with a ! Played around a bit with it and noticed that egui's fps counter goes up during resizing. In fact if I always enable present with transaction, it renders 240fps instead of the expected 120fps in continuous mode. That kinda explains how this can work: the screen (and presumably resize events) happen at 120fps and we're practically running into a sampling problem of the currently correct screen size. Fascinating! Unfortunately the jitter is still there a liiiitttle bit on external monitors. But on the builtin one it's gone completely. Interestingly, it's the entire window jittering which never happens on the internal monitor - notice how the top left of the windows moves as well (running via usbc @ 120hz): occasional.jitter.on.external.monitor.downsized.mov
Reading through the material I come to the same conclusion as well so far and agree that the proposed solution is the way to go forward. Let's make sure to have everything documented in the code! I can explore a bit how we can expose this in wgpu directly more easily, but the next time we can land semvar breaking changes is end of december, would be nice to figure this out without that :) |
|
Looking around a bit, doing platform specific features on Either way, we're probably better off either..
|
|
I was not able to understand why the fps increases 2x. I tried to poke around w/ the debugger, even into places I am not supposed to poke debuggers in. No avail. I find it weird as I expect CADisplayLink (my educated guess what drives the render loop) to be locked to current display preferred FPS. In the power safe mode it's supposed to be 60fps, not 120fps it does.
I doubt that it's a sampling problem caused by fps mismatch but rather, I think, it's just about 'clock' sync. But that's only intuition I can put on the table as I have no data to support it. Maybe it can be checked by increasing FPS forcefull w/ (EDIT: without!) But anyway. As for the solution, I agree. I want to spell out the possible ways to go:
B) Introduce the
C) Introduce the
D) Enable wgpu/metal on osx target as required dependency.
I'd vote for (A) if it can be done as a non-breaking API change, and (B) as it fixes the defaulting to metal issue and allows future backend specific configurations. And those are not muturaly-exclusive. What do you think? Lastly we are ignoring an elephant in the room. Why on earth the
Osx/ios development experience helps there, still it will take me a while. |
|
A)
It uses
no longer seeing how we could pull that off, very tricky B)
It can be part of it but it won't solve it single handed. The problem is that we want users to pick what wgpu backends they use but come with nice defaults. See also #7344
We should absolutely not mirror all wgpu feature flags if we can avoid it. Clutters the feature list with things that are perfectly fine for wgpu to own & document. Also, don't get why we'd have to - the thing we want to potentially feature gate is the special metal-resize behavior.... which is why I think the
don't follow. If someone enables wgpu but doesn't enable it on any egui crate then egui doesn't know about metal, i.e. the resize fix here won't be enabled. But that shouldn't cause any complication issues per se? C)
🤷 😉 D)
Agree that it's rare but just as a sidenotes I think for games it makes the least sense. The main scenario I can think of is that you have a non-game application and therefore don't care about the metal backend and rather run OpenGL on all platforms to avoid surprises.
Yeah that would be good to investigate :/. No clue! Would be indeed awesome if you could unearth some insight there! |
|
I am happy to announce my investigation of the issue in The PR is prepared for review. Please note:
Other than that - seems it works. ^__^ |
|
By the way, how is this handsome FPS stats overlay called, how to enable it? : ) |
huh yeah right now you could only opt out if you depend on egui-wgpu but for some reason not on eframe. Kinda limited. Ah well we can take care if that if someone complains. Very unlikely... |
Learned it from one of the articles you linked :). It's |
Wumpf
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
really nicely done, thank you!
| Self::configure_surface( | ||
| state, | ||
| self.render_state.as_ref().unwrap(), | ||
| &self.configuration, | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this part can go outside of the unsafe scope, no need to have that longer than necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.. but it's actually annoying to do so with the condition an all. Whatever (:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's exactly what I have been moving-back-n-forth for 15minutes w/o any even remote satisfaction, agree.
Co-authored-by: Emil Ernerfeldt <[email protected]>
Changes in snapshot images should be pixel-alignment improvements thanks to * emilk/egui#7710 --- ## egui changelog ### ⭐ Added * Add `Plugin::on_widget_under_pointer` to support widget inspector [#7652](emilk/egui#7652) by [@juancampa](https://github.com/juancampa) * Add `Response::total_drag_delta` and `PointerState::total_drag_delta` [#7708](emilk/egui#7708) by [@emilk](https://github.com/emilk) ### 🔧 Changed * Improve accessibility and testability of `ComboBox` [#7658](emilk/egui#7658) by [@lucasmerlin](https://github.com/lucasmerlin) ### 🐛 Fixed * Fix `profiling::scope` compile error when profiling using `tracing` backend [#7646](emilk/egui#7646) by [@PPakalns](https://github.com/PPakalns) * Fix edge cases in "smart aiming" in sliders [#7680](emilk/egui#7680) by [@emilk](https://github.com/emilk) * Hide scroll bars when dragging other things [#7689](emilk/egui#7689) by [@emilk](https://github.com/emilk) * Prevent widgets sometimes appearing to move relative to each other [#7710](emilk/egui#7710) by [@emilk](https://github.com/emilk) * Fix `ui.response().interact(Sense::click())` being flakey [#7713](emilk/egui#7713) by [@lucasmerlin](https://github.com/lucasmerlin) ## eframe changelog * Fix jittering during window resize on MacOS for WGPU/Metal [#7641](emilk/egui#7641) by [@aspcartman](https://github.com/aspcartman) * Make sure `native_pixels_per_point` is set during app creation [#7683](emilk/egui#7683) by [@emilk](https://github.com/emilk) --------- Co-authored-by: Lucas Meurer <[email protected]> Co-authored-by: lucasmerlin <[email protected]>
Overview
As mentioned in #903 the window resizing on macos in egui was hit by something heavy some long time ago due to, probably, changes in winit 0.29->0.30. I have not been able to narrow down to the root cause of that back then. But even though the issues were reported for glow initially, they are present with winit/wgpu/metal triplet as well today.
A.mp4
But it appears for the metal the solution is well known. (thx @mattia-marini #903 (comment))
The CAMetalLayer - one that wgpu draws to - has to draw the frames in sync with CoreAnimation, framework that drives the whole GUI rendering pipeline on apple platforms. The desynchronization between the two pipelines lead to the visual jitter. CA is unaware that a new frame is coming and tries to stretch the existing window content.
This behavior is controlled by presentsWithTransaction flag. The support for it has been implemented in wgpu many years ago, 2021 it seems, including the needed drawing logic alterations, and is exposed to the metal backend users as a pub field in the metal surface struct. Note it was not escalated to higher levels of abstraction as this is a backend-specific feature.
Yet enabling it comes with side-effects, best described in Zed GPUI Article and potential latencies (@Wumpf #903 (comment), gfx-rs/wgpu#8109). The metal layer is desynced with CA for most performance and control, but that limits the ability to play well with native UI elements, including window itself.
So the proposed solution is to enable the
present_with_transactiononly during the resizing - same solution as Zed did. I've made a prototype and.. Well. It works as expected.B.mp4
I have made changes to
egui-wgpu: madePainterresponsible for toggling the surface properties in response to window resize begin & end; and toeframewgpu integration: detection of resize start and end w/ pokes to the painter.Things to address
Accessing the field
The field is currently accessed through a pointer cast. The API is specific for Metal and thus is not exposed to upper levels, so if one has no typed handle to the metal surface struct one has to downcast.
I think it won't change on the wgpu side in the future. It's a stable API for 4 years already :)
So having this abomination around might need an explicit approval. Personally I am completely fine with it.
Conditional compilation
The
wgpu::hal::api::Metalandwgpu::hal::metal::Surfaceare undermetalflag in wgpu crate. Currently egui does not import wgpu backend by default or has feature flags for that. That's what defaulting to wgpu in eframe & #7615 recently hit into.This PR also needs backend feature flags in egui for it to work as it depends on those metal types. The
#[cfg(all(target_os = "macos"))]is not enough and code won't compile if
wgpu/metalis not enabled. But it's impossible to cfg('wgpu/metal') afaik. Any solutions? Should it wait @emilk for the metal flag?Window lifecycle events
Winit does not provide events for window resizing other than just a plain
Resized(new_size). In context of the issue at hand it's required to be aware that resize is in progress across frames. It seems that during the window resize winit fires an exclusive stream ofResizedevents. I have assumed that only one window viewport can be resized at a single moment of time and added tracking of it's id. When an event arrives and it's not a resize - well, resize has ended it seems.It is safe for a random event to occur during the resizing and trigger the end prematurely. That will only lead to a possibility of a visual jitter occurrence at that frame.
Is it fine?
PS: So happy this thing stopped lagging on resize, it's a miracle. ^_^
PSS: The current draft will not build in CI because of the feature flag thingy.