From dbd95ea7427f2cdca33053e5c8b9b4d012d257d9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 25 Mar 2026 11:32:57 +0100 Subject: [PATCH] gpui_linux: Force scene rebuild after GPU device recovery (#52389) After GPU device recovery clears the atlas, the next frame could re-present a stale scene via the needs_present path (which skips scene rebuilding). The stale scene references texture IDs that no longer exist in the cleared atlas, causing an index-out-of-bounds panic. Fix this by setting a force_render_after_recovery flag when device recovery completes. The platform refresh loop reads this flag and passes force_render: true in RequestFrameOptions, ensuring GPUI rebuilds the scene before presenting. Fixes ZED-5QT Release Notes: - N/A or Added/Fixed/Improved ... --- crates/gpui_linux/src/linux/wayland/window.rs | 10 +++++++++- crates/gpui_linux/src/linux/x11/client.rs | 5 ++++- crates/gpui_linux/src/linux/x11/window.rs | 3 +++ crates/gpui_windows/src/events.rs | 9 +++++---- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index c79fbbb3eb2f5ec99b808d6265d6536aa72a7a18..f3a5322f25654492d66c26d037f9da5279dac3aa 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -116,6 +116,7 @@ pub struct WaylandWindowState { handle: AnyWindowHandle, active: bool, hovered: bool, + pub(crate) force_render_after_recovery: bool, in_progress_configure: Option, resize_throttle: bool, in_progress_window_controls: Option, @@ -389,6 +390,7 @@ impl WaylandWindowState { handle, active: false, hovered: false, + force_render_after_recovery: false, in_progress_window_controls: None, window_controls: WindowControls::default(), client_inset: None, @@ -570,11 +572,16 @@ impl WaylandWindowStatePtr { let mut state = self.state.borrow_mut(); state.surface.frame(&state.globals.qh, state.surface.id()); state.resize_throttle = false; + let force_render = state.force_render_after_recovery; + state.force_render_after_recovery = false; drop(state); let mut cb = self.callbacks.borrow_mut(); if let Some(fun) = cb.request_frame.as_mut() { - fun(Default::default()); + fun(RequestFrameOptions { + force_render, + ..Default::default() + }); } } @@ -1372,6 +1379,7 @@ impl PlatformWindow for WaylandWindow { // The current scene references atlas textures that were cleared during recovery. // Skip this frame and let the next frame rebuild the scene with fresh textures. + state.force_render_after_recovery = true; return; } diff --git a/crates/gpui_linux/src/linux/x11/client.rs b/crates/gpui_linux/src/linux/x11/client.rs index afe6ee129cbeb393594cf661349bd76bde34ceab..05d4ebd1bacce7e955b48a218e93b432182b75d4 100644 --- a/crates/gpui_linux/src/linux/x11/client.rs +++ b/crates/gpui_linux/src/linux/x11/client.rs @@ -1883,11 +1883,14 @@ impl X11ClientState { if let Some(window) = state.windows.get_mut(&x_window) { let expose_event_received = window.expose_event_received; window.expose_event_received = false; + let force_render = std::mem::take( + &mut window.window.state.borrow_mut().force_render_after_recovery, + ); let window = window.window.clone(); drop(state); window.refresh(RequestFrameOptions { require_presentation: expose_event_received, - force_render: false, + force_render, }); } xcb_connection diff --git a/crates/gpui_linux/src/linux/x11/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index e0095716c8fd0670ad93484fb8a21a5b86ed4a43..31d1dbacb114f2a9f760f94a898ba37e582cf12e 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -277,6 +277,7 @@ pub struct X11WindowState { hidden: bool, active: bool, hovered: bool, + pub(crate) force_render_after_recovery: bool, fullscreen: bool, client_side_decorations_supported: bool, decorations: WindowDecorations, @@ -750,6 +751,7 @@ impl X11WindowState { input_handler: None, active: false, hovered: false, + force_render_after_recovery: false, fullscreen: false, maximized_vertical: false, maximized_horizontal: false, @@ -1637,6 +1639,7 @@ impl PlatformWindow for X11Window { // The current scene references atlas textures that were cleared during recovery. // Skip this frame and let the next frame rebuild the scene with fresh textures. + inner.force_render_after_recovery = true; return; } diff --git a/crates/gpui_windows/src/events.rs b/crates/gpui_windows/src/events.rs index 3506ae2a2cc22d57c4cefba1a4c5a1850c411453..985989a4c98dcaafa35661b0a496dcadf42665d3 100644 --- a/crates/gpui_windows/src/events.rs +++ b/crates/gpui_windows/src/events.rs @@ -1143,10 +1143,11 @@ impl WindowsWindowInner { fn draw_window(&self, handle: HWND, force_render: bool) -> Option { let mut request_frame = self.state.callbacks.request_frame.take()?; - // we are instructing gpui to force render a frame, this will - // re-populate all the gpu textures for us so we can resume drawing in - // case we disabled drawing earlier due to a device loss - self.state.renderer.borrow_mut().mark_drawable(); + if force_render { + // Re-enable drawing after a device loss recovery. The forced render + // will rebuild the scene with fresh atlas textures. + self.state.renderer.borrow_mut().mark_drawable(); + } request_frame(RequestFrameOptions { require_presentation: false, force_render,