gpui_windows: Force scene rebuild after GPU device recovery

Lukas Wirth created

Same issue as on Linux: after device recovery clears the atlas, a
WM_PAINT arriving during the 200ms recovery sleep could call
draw_window with force_render=false, which unconditionally called
mark_drawable() (clearing skip_draws) and then presented a stale
scene against the empty atlas.

Fix this by adding a force_render_after_recovery flag to the window
state, set during handle_device_lost. The first draw_window call
after recovery reads this flag and forces a full scene rebuild,
matching the Linux approach.

Change summary

crates/gpui_windows/src/events.rs | 11 +++++++----
crates/gpui_windows/src/window.rs |  2 ++
2 files changed, 9 insertions(+), 4 deletions(-)

Detailed changes

crates/gpui_windows/src/events.rs 🔗

@@ -1136,6 +1136,7 @@ impl WindowsWindowInner {
         {
             panic!("Device lost: {err}");
         }
+        self.state.force_render_after_recovery.set(true);
         Some(0)
     }
 
@@ -1143,10 +1144,12 @@ impl WindowsWindowInner {
     fn draw_window(&self, handle: HWND, force_render: bool) -> Option<isize> {
         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();
+        let force_render = force_render || self.state.force_render_after_recovery.take();
+        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,

crates/gpui_windows/src/window.rs 🔗

@@ -59,6 +59,7 @@ pub struct WindowsWindowState {
     pub hovered: Cell<bool>,
 
     pub renderer: RefCell<DirectXRenderer>,
+    pub force_render_after_recovery: Cell<bool>,
 
     pub click_state: ClickState,
     pub current_cursor: Cell<Option<HCURSOR>>,
@@ -149,6 +150,7 @@ impl WindowsWindowState {
             last_reported_capslock: Cell::new(last_reported_capslock),
             hovered: Cell::new(hovered),
             renderer: RefCell::new(renderer),
+            force_render_after_recovery: Cell::new(false),
             click_state,
             current_cursor: Cell::new(current_cursor),
             nc_button_pressed: Cell::new(nc_button_pressed),