From 301075d53457a43b7a739f44f644e3d95f71ae81 Mon Sep 17 00:00:00 2001 From: "zed-zippy[bot]" <234243425+zed-zippy[bot]@users.noreply.github.com> Date: Fri, 27 Mar 2026 09:14:55 +0000 Subject: [PATCH] gpui_linux: Force scene rebuild after GPU device recovery (#52389) (cherry-pick to stable) (#52400) Cherry-pick of #52389 to stable ---- 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 ... Co-authored-by: Lukas Wirth --- 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 71a4ee2ab5033a69c5872fab631fd13af6c82b0e..d2883dc3ac64f52a38ac496b025da6be45246a85 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -114,6 +114,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, @@ -387,6 +388,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, @@ -568,11 +570,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() + }); } } @@ -1367,6 +1374,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 1f8db390029d67d8cdc17da7800a0f8e1d5e1af9..1cb8a6cecdb9f13e9e47bc764ba6be3314e02b60 100644 --- a/crates/gpui_linux/src/linux/x11/client.rs +++ b/crates/gpui_linux/src/linux/x11/client.rs @@ -1871,11 +1871,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 57600103ce9ec1a67abb4abc373b0ed4c26cb077..a1df1c9f9a839da62c56e8b4d0dc21ae42e0e046 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -275,6 +275,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, @@ -738,6 +739,7 @@ impl X11WindowState { input_handler: None, active: false, hovered: false, + force_render_after_recovery: false, fullscreen: false, maximized_vertical: false, maximized_horizontal: false, @@ -1623,6 +1625,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,