diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index 1e3af66c59858c435ca3da093a1c48056b77667e..9f2556fc2aea66fc127043950f79c7091ae11b46 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -1385,6 +1385,10 @@ impl PlatformWindow for WaylandWindow { } state.renderer.draw(scene); + + if state.renderer.needs_redraw() { + state.force_render_after_recovery = true; + } } fn completed_frame(&self) { diff --git a/crates/gpui_linux/src/linux/x11/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index c21d8baf31de06e6fe155678c5243c21ec6cba53..285ba8802db7444eba92182ccac6139b41a52fb0 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -1680,6 +1680,10 @@ impl PlatformWindow for X11Window { } inner.renderer.draw(scene); + + if inner.renderer.needs_redraw() { + inner.force_render_after_recovery = true; + } } fn sprite_atlas(&self) -> Arc { diff --git a/crates/gpui_wgpu/src/wgpu_atlas.rs b/crates/gpui_wgpu/src/wgpu_atlas.rs index bef69878adafb71d47a1cbe222fc1454169b8c3b..f3b9de1ca82e16b3a0ebdb1b852f85bd49fa72f7 100644 --- a/crates/gpui_wgpu/src/wgpu_atlas.rs +++ b/crates/gpui_wgpu/src/wgpu_atlas.rs @@ -82,6 +82,15 @@ impl WgpuAtlas { } } + /// Clears all cached textures and tiles, forcing them to be recreated. + /// Use this for incremental recovery when the device is still valid. + pub fn clear(&self) { + let mut lock = self.0.lock(); + lock.storage = WgpuAtlasStorage::default(); + lock.tiles_by_key.clear(); + lock.pending_uploads.clear(); + } + /// Handles device lost by clearing all textures and cached tiles. /// The atlas will lazily recreate textures as needed on subsequent frames. pub fn handle_device_lost(&self, context: &WgpuContext) { diff --git a/crates/gpui_wgpu/src/wgpu_renderer.rs b/crates/gpui_wgpu/src/wgpu_renderer.rs index c38de02707f9daf45b37c418f8c58fcd1805e29d..39b6f3f7bdfee9f3f95a6111b66a7c4824c0aa6c 100644 --- a/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -121,6 +121,15 @@ struct WgpuResources { path_msaa_view: Option, } +impl WgpuResources { + fn invalidate_intermediate_textures(&mut self) { + self.path_intermediate_texture = None; + self.path_intermediate_view = None; + self.path_msaa_texture = None; + self.path_msaa_view = None; + } +} + pub struct WgpuRenderer { /// Shared GPU context for device recovery coordination (unused on WASM). #[allow(dead_code)] @@ -146,6 +155,7 @@ pub struct WgpuRenderer { failed_frame_count: u32, device_lost: std::sync::Arc, surface_configured: bool, + needs_redraw: bool, } impl WgpuRenderer { @@ -474,6 +484,7 @@ impl WgpuRenderer { failed_frame_count: 0, device_lost: context.device_lost_flag(), surface_configured: true, + needs_redraw: false, }) } @@ -973,10 +984,7 @@ impl WgpuRenderer { // Invalidate intermediate textures - they will be lazily recreated // in draw() after we confirm the surface is healthy. This avoids // panics when the device/surface is in an invalid state during resize. - resources.path_intermediate_texture = None; - resources.path_intermediate_view = None; - resources.path_msaa_texture = None; - resources.path_msaa_view = None; + resources.invalidate_intermediate_textures(); } } @@ -1077,10 +1085,19 @@ impl WgpuRenderer { if let Some(error) = last_error { self.failed_frame_count += 1; log::error!( - "GPU error during frame (failure {} of 20): {error}", + "GPU error during frame (failure {} of 10): {error}", self.failed_frame_count ); - if self.failed_frame_count > 20 { + + // TBD. Does retrying more actually help? + if self.failed_frame_count > 5 { + if let Some(res) = self.resources.as_mut() { + res.invalidate_intermediate_textures(); + } + self.atlas.clear(); + self.needs_redraw = true; + return; + } else if self.failed_frame_count > 10 { panic!("Too many consecutive GPU errors. Last error: {error}"); } } else { @@ -1668,10 +1685,7 @@ impl WgpuRenderer { self.surface_configured = false; // Drop intermediate textures since they reference the old surface size. if let Some(res) = self.resources.as_mut() { - res.path_intermediate_texture = None; - res.path_intermediate_view = None; - res.path_msaa_texture = None; - res.path_msaa_view = None; + res.invalidate_intermediate_textures(); } } @@ -1721,10 +1735,7 @@ impl WgpuRenderer { res.surface = surface; // Invalidate intermediate textures — they'll be recreated lazily. - res.path_intermediate_texture = None; - res.path_intermediate_view = None; - res.path_msaa_texture = None; - res.path_msaa_view = None; + res.invalidate_intermediate_textures(); } self.surface_configured = true; @@ -1743,6 +1754,12 @@ impl WgpuRenderer { self.device_lost.load(std::sync::atomic::Ordering::SeqCst) } + /// Returns true if a redraw is needed because GPU state was cleared. + /// Calling this method clears the flag. + pub fn needs_redraw(&mut self) -> bool { + std::mem::take(&mut self.needs_redraw) + } + /// Recovers from a lost GPU device by recreating the renderer with a new context. /// /// Call this after detecting `device_lost()` returns true.