Stabilize native viewport lifecycle and macOS wgpu fullscreen transitions - #8280
Stabilize native viewport lifecycle and macOS wgpu fullscreen transitions#8280yay wants to merge 2 commits into
Conversation
|
Preview available at https://egui-pr-preview.github.io/pr/8280-steadymacos-wgpu-fullscreen-resize View snapshot changes at kitdiff |
0863bb1 to
d0ba7f2
Compare
I asked Fable for a comment:Possibly useful corroboration for the "extra ghost window" you documented here: while working on #8259 (native child viewport opened from a fullscreen root, fix in #8286), the baseline repro independently produced the same stale extra window — through a different path than the deferred-viewport fullscreen exit shown in your video. The trigger there: root in native fullscreen, then a native child viewport is created → macOS starts a Space/Split View negotiation → the child gets tiled into the fullscreen Space (its styleMask actually gains FullScreen without the app ever requesting it), and one stale untitled window is left behind. [AppKit state dump] So the ghost window seems reachable from any AppKit-driven Space negotiation, not only from fullscreen exits — which supports this PR's diagnosis that eframe state updates during AppKit-driven transitions are the underlying hazard. #8286 avoids that negotiation at the source for the creation-while-fullscreen case… The two changes should be complementary: this PR stabilizes transitions that do happen; #8286 prevents one class of them from being triggered accidentally. Happy to re-run my repro on top of this branch if a combined test would help. |
|
@smoe Thanks. I tested the same flow in my app on top of this PR, but putting the root viewport in fullscreen and then creating a deferred child did not produce a ghost window, abort fullscreen, or trigger visible Split View negotiation. I did find a separate issue when closing a child while the fullscreen root was in another Space (child window turned black without closing). That turned out to be caused by using root visibility as a proxy for whether root UI had evaluated the viewport tree, so this PR now tracks that explicitly. So my test does not currently corroborate that the ghost windows in the two reproductions share an underlying cause. The PRs still appear complementary at the implementation level, but testing your exact reproduction on top of #8280 would be useful to check if there's an overlap. |
879be28 to
32e0b7f
Compare
Fable kindly helped out with this comment again.Ran the exact #8259 reproduction (fullscreen the root via
So on this OS the two reproductions don't share a cause at the window-creation step: #8280 doesn't change window creation, and the Space negotiation fires exactly as on 0.35.0. Two observations that may explain why your test didn't reproduce it:
The good news is symmetrical: the ghost window in your reproduction (deferred viewport fullscreen exits) is plausibly fixed by this PR's pruning-authority change and out of #8286's reach, while the creation-time negotiation is prevented by #8286 and out of this PR's reach — and the merged run confirms they compose cleanly. Repro source below. Repro harness (main.rs)//! Repro/verification for #8259: use std::time::Instant; use eframe::egui; struct TestApp { fn dump_windows(label: &str) { impl eframe::App for TestApp { } fn main() -> eframe::Result { |
|
@smoe Thanks for testing it. |
Drive Metal presentation from AppKit transition state, represent viewport-output completeness explicitly, preserve deferred viewport lifecycle across partial passes, and retry painting when the first surface acquisition is skipped.
32e0b7f to
2091af2
Compare
What started as an investigation into deferred viewport issues during macOS fullscreen transitions uncovered broader problems in how egui and eframe handle native viewport lifecycle.
This PR builds on #8226 by covering the remaining cases where a pass cannot authoritatively determine the complete viewport set.
Issues
Extra ghost window sporadically showing up after exiting fullscreen:

Bad viewport scaling during exit fullscreen animation:

Stretched content during enter full screen animation, with stale content in the bottom left corner:

In a multi-viewport macOS app using eframe and either wgpu or glow, native fullscreen transitions for secondary viewports could produce several failures:
Video of egui 0.35.0 deferred viewport example behavior on macOS Sequoia 15.7.7: https://youtu.be/4vWCNajjFts
The video shows it can sometimes take a few tries to reproduce certain issues, but they inevitably happen.
One notable issue in the linked video: after a deferred viewport exits fullscreen, macOS can leave behind what looks like an extra native window. That window is not an egui viewport that the app still owns intentionally. It is stale state caused by eframe pruning or updating deferred viewport state from a partial child viewport pass while AppKit is still driving the fullscreen transition.
This PR avoids that by treating only root passes whose viewport UI ran as authoritative for pruning native viewports, and by preserving existing deferred viewport callbacks when incomplete child passes do not report them.
On macOS empirical testing shows that fullscreen windows are sometimes reported as:
Fixes
This changes egui and its native integrations to:
API migration
FullOutput::viewport_outputis now aViewportOutputReportso integrations cannotsilently ignore whether a viewport report is authoritative.
output.viewport_output.entriesfor the viewport map.output.viewport_output.is_completebefore removing missing native viewports.raw_input.viewport_ui_enabled = falsewhen running a pass without evaluating viewport UI.Dependency
Depends on rust-windowing/winit#4610 for:
WindowExtMacOS::is_live_resizing()WindowExtMacOS::is_fullscreen_transition()WindowDelegate::is_maximized()Depends on gfx-rs/wgpu#9828 for:
Because egui 0.35 uses wgpu 29 while the upstream wgpu PR targets trunk, this branch temporarily pins an equivalent v29 backport in
yay/wgpu.This is draft because the required winit and wgpu APIs have not reached versions compatible with egui 0.35. macOS checks may fail until those dependency updates are available.