From c6514d7397b4f00bdbfdfee7cceea511558b1b1c Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 4 May 2026 20:24:23 -0600 Subject: [PATCH] Try to reduce linux wGPU crashes better (#55343) Updates #54349 There were two problems: * The crash never happened, instead we'd always retry. * When re-trying it seemed like we were picking llvmpipe. Claude's suggestion was that immediately after wake, the real GPU isn't yet awake, and so we pick llvm. Avoid this by disallowing llvmpipe on retry Release Notes: - linux: Reduced crash rate when recovering GPUs --- crates/gpui_linux/src/linux/wayland/window.rs | 15 +++--- crates/gpui_linux/src/linux/x11/window.rs | 15 +++--- crates/gpui_wgpu/src/wgpu_context.rs | 47 ++++++++++++++++--- crates/gpui_wgpu/src/wgpu_renderer.rs | 15 ++++-- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index 5d53496c05..857289a1d9 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -1387,16 +1387,13 @@ impl PlatformWindow for WaylandWindow { .display_ptr() .cast::(), }; - state.renderer.recover(&raw_window).unwrap_or_else(|err| { - panic!( - "GPU device lost and recovery failed. \ - This may happen after system suspend/resume. \ - Please restart the application.\n\nError: {err}" - ) - }); + match state.renderer.recover(&raw_window) { + Ok(()) => {} + Err(err) => { + log::warn!("GPU recovery failed, will retry on next frame: {err}"); + } + } - // 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/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index b44ac2faf4..325d70eb31 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -1670,16 +1670,13 @@ impl PlatformWindow for X11Window { window_id: self.0.x_window, visual_id: inner.visual_id, }; - inner.renderer.recover(&raw_window).unwrap_or_else(|err| { - panic!( - "GPU device lost and recovery failed. \ - This may happen after system suspend/resume. \ - Please restart the application.\n\nError: {err}" - ) - }); + match inner.renderer.recover(&raw_window) { + Ok(()) => {} + Err(err) => { + log::warn!("GPU recovery failed, will retry on next frame: {err}"); + } + } - // 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_wgpu/src/wgpu_context.rs b/crates/gpui_wgpu/src/wgpu_context.rs index 80900311b2..d25e1dc71c 100644 --- a/crates/gpui_wgpu/src/wgpu_context.rs +++ b/crates/gpui_wgpu/src/wgpu_context.rs @@ -28,6 +28,25 @@ impl WgpuContext { instance: wgpu::Instance, surface: &wgpu::Surface<'_>, compositor_gpu: Option, + ) -> anyhow::Result { + Self::new_with_options(instance, surface, compositor_gpu, false) + } + + #[cfg(not(target_family = "wasm"))] + pub fn new_rejecting_software( + instance: wgpu::Instance, + surface: &wgpu::Surface<'_>, + compositor_gpu: Option, + ) -> anyhow::Result { + Self::new_with_options(instance, surface, compositor_gpu, true) + } + + #[cfg(not(target_family = "wasm"))] + fn new_with_options( + instance: wgpu::Instance, + surface: &wgpu::Surface<'_>, + compositor_gpu: Option, + reject_software: bool, ) -> anyhow::Result { let device_id_filter = match std::env::var("ZED_DEVICE_ID") { Ok(val) => parse_pci_id(&val) @@ -49,6 +68,7 @@ impl WgpuContext { device_id_filter, surface, compositor_gpu.as_ref(), + reject_software, ))?; let device_lost = Arc::new(AtomicBool::new(false)); @@ -197,6 +217,7 @@ impl WgpuContext { device_id_filter: Option, surface: &wgpu::Surface<'_>, compositor_gpu: Option<&CompositorGpuHint>, + reject_software: bool, ) -> anyhow::Result<( wgpu::Adapter, wgpu::Device, @@ -244,12 +265,16 @@ impl WgpuContext { _ => 1, }; - let type_priority: u8 = match info.device_type { - wgpu::DeviceType::DiscreteGpu => 0, - wgpu::DeviceType::IntegratedGpu => 1, - wgpu::DeviceType::Other => 2, - wgpu::DeviceType::VirtualGpu => 3, - wgpu::DeviceType::Cpu => 4, + let type_priority: u8 = if info.device_type == wgpu::DeviceType::Cpu { + 4 + } else { + match info.device_type { + wgpu::DeviceType::DiscreteGpu => 0, + wgpu::DeviceType::IntegratedGpu => 1, + wgpu::DeviceType::Other => 2, + wgpu::DeviceType::VirtualGpu => 3, + wgpu::DeviceType::Cpu => 4, + } }; let backend_priority: u8 = match info.backend { @@ -284,6 +309,16 @@ impl WgpuContext { // Test each adapter by creating a device and configuring the surface for adapter in adapters { let info = adapter.get_info(); + + if reject_software && info.device_type == wgpu::DeviceType::Cpu { + log::info!( + "Skipping software renderer: {} ({:?})", + info.name, + info.backend + ); + continue; + } + log::info!("Testing adapter: {} ({:?})...", info.name, info.backend); match Self::try_adapter_with_surface(&adapter, surface).await { diff --git a/crates/gpui_wgpu/src/wgpu_renderer.rs b/crates/gpui_wgpu/src/wgpu_renderer.rs index 7377c73fea..da7e71c726 100644 --- a/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -1097,15 +1097,16 @@ impl WgpuRenderer { ); // TBD. Does retrying more actually help? - if self.failed_frame_count > 5 { + if self.failed_frame_count > 10 { + panic!("Too many consecutive GPU errors. Last error: {error}"); + } else 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; + self.failed_frame_count = 0; return; - } else if self.failed_frame_count > 10 { - panic!("Too many consecutive GPU errors. Last error: {error}"); } } else { self.failed_frame_count = 0; @@ -1799,12 +1800,16 @@ impl WgpuRenderer { self.resources = None; *gpu_context.borrow_mut() = None; - // Wait for GPU driver to stabilize (350ms copied from windows :shrug:) + // Wait briefly for the GPU driver to stabilize, then try to + // recreate the context without software renderers. If this fails + // the caller should request another frame and retry — the real GPU + // may need more time to come back (e.g. after suspend/resume). std::thread::sleep(std::time::Duration::from_millis(350)); let instance = WgpuContext::instance(Box::new(window.clone())); let surface = create_surface(&instance, window_handle.as_raw())?; - let new_context = WgpuContext::new(instance, &surface, self.compositor_gpu)?; + let new_context = + WgpuContext::new_rejecting_software(instance, &surface, self.compositor_gpu)?; *gpu_context.borrow_mut() = Some(new_context); surface } else {