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
This commit is contained in:
Conrad Irwin authored and GitHub committed 2026-05-05 02:24:23 +00:00
1 parent ba6d258b0f
commit c6514d7397
4 files changed
+63 -29

No files matched your search

@@ -1387,16 +1387,13 @@ impl PlatformWindow for WaylandWindow {
.display_ptr()
.cast::<std::ffi::c_void>(),
};
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;
}
+6 -9
View File
@@ -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;
}
+41 -6
View File
@@ -28,6 +28,25 @@ impl WgpuContext {
instance: wgpu::Instance,
surface: &wgpu::Surface<'_>,
compositor_gpu: Option<CompositorGpuHint>,
) -> anyhow::Result<Self> {
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<CompositorGpuHint>,
) -> anyhow::Result<Self> {
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<CompositorGpuHint>,
reject_software: bool,
) -> anyhow::Result<Self> {
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<u32>,
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 {
+10 -5
View File
@@ -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 {