1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Fix random hangs by improving wgpu::Surface lifecycle handling (#8171)

### Related

* Closes #8134.
* Related to #5136.

Possibly fixes:

* #8123
* #5145 

### What

We did not properly handle the variants of
[`CurrentSurfaceTexture`](https://docs.rs/wgpu/latest/wgpu/enum.CurrentSurfaceTexture.html)
and always returned `SkipFrame`.

Because of this `egui` could end up in a state where frames are always
skipped after observing `Outdated`, without the chance to recover
(unless an event arrives from the outside).

> [!NOTE]
> This is not Wayland-specific, but could happen on all platforms. It
just happens frequently for Wayland compositors that directly resize a
window after creation (such as tiling/scrolling compositors like
`hyprland` and `niri`).

This PR improves this by separating the code paths for `Outdated` and
`Lost`, to help recover from those events.
This commit is contained in:
Jochen Görtler
2026-05-19 11:06:09 +02:00
committed by GitHub
parent 82aaef3530
commit 7dba2e99fa
3 changed files with 163 additions and 45 deletions

View File

@@ -313,7 +313,17 @@ pub enum SurfaceErrorAction {
/// Do nothing and skip the current frame.
SkipFrame,
/// Instructs egui to recreate the surface, then skip the current frame.
/// Reconfigure the existing surface, then skip the current frame.
///
/// Calls [`wgpu::Surface::configure`] on the current surface object.
/// Use for [`wgpu::CurrentSurfaceTexture::Outdated`].
Reconfigure,
/// Drop the surface, create a new one via [`wgpu::Instance::create_surface`], configure it,
/// then skip the current frame.
///
/// Use for [`wgpu::CurrentSurfaceTexture::Lost`], where reconfiguring the same surface
/// object cannot recover.
RecreateSurface,
}
@@ -376,23 +386,28 @@ impl Default for WgpuConfiguration {
// No display handle available at this point — callers should replace this with
// `WgpuSetup::from_display_handle(...)` before creating the instance if one is available.
wgpu_setup: WgpuSetup::without_display_handle(),
on_surface_status: Arc::new(|status| {
match status {
wgpu::CurrentSurfaceTexture::Outdated => {
// This error occurs when the app is minimized on Windows.
// Silently return here to prevent spamming the console with:
// "The underlying surface has changed, and therefore the swap chain must be updated"
}
wgpu::CurrentSurfaceTexture::Occluded => {
// This error occurs when the application is occluded (e.g. minimized or behind another window).
log::debug!("Dropped frame with error: {status:?}");
}
_ => {
log::warn!("Dropped frame with error: {status:?}");
}
on_surface_status: Arc::new(|status| match status {
wgpu::CurrentSurfaceTexture::Outdated => {
// The compositor changed the surface (resize, scale, output, …). wgpu
// requires us to reconfigure before the next acquire. Skipping would mean
// we are stuck in `Outdated` forever.
log::trace!("Dropped frame with error: {status:?}");
SurfaceErrorAction::Reconfigure
}
wgpu::CurrentSurfaceTexture::Lost => {
// The underlying surface is gone and we need a fresh one from the `wgpu::Instance`.
log::debug!("Dropped frame with error: {status:?}");
SurfaceErrorAction::RecreateSurface
}
wgpu::CurrentSurfaceTexture::Occluded => {
// App is hidden (minimized / behind another window). Skip silently.
log::trace!("Skipping frame due to occlusion.");
SurfaceErrorAction::SkipFrame
}
_ => {
log::warn!("Dropped frame with error: {status:?}");
SurfaceErrorAction::SkipFrame
}
SurfaceErrorAction::SkipFrame
}),
}
}