From ffa489a846346af427f6b09642bd8b2cfb016de6 Mon Sep 17 00:00:00 2001 From: Nicola Papale Date: Sat, 12 Nov 2022 08:06:56 +0000 Subject: [PATCH] Ignore `Timeout` errors on Linux AMD & Intel (#5957) # Objective - Fix #3606 - Fix #4579 - Fix #3380 ## Solution When running on a Linux machine with some AMD or Intel device, when calling `surface.get_current_texture()`, ignore `wgpu::SurfaceError::Timeout` errors. ## Alternative An alternative solution found in the `wgpu` examples is: ```rust let frame = surface .get_current_texture() .or_else(|_| { render_device.configure_surface(surface, &swap_chain_descriptor); surface.get_current_texture() }) .expect("Error reconfiguring surface"); window.swap_chain_texture = Some(TextureView::from(frame)); ``` See: Veloren [handles the Timeout error the way this PR proposes to handle it](https://github.com/gfx-rs/wgpu/issues/1218#issuecomment-1092056971). The reason I went with this PR's solution is that `configure_surface` seems to be quite an expensive operation, and it would run every frame with the wgpu framework solution, despite the fact it works perfectly fine without `configure_surface`. I know this looks super hacky with the linux-specific line and the AMD check, but my understanding is that the `Timeout` occurrence is specific to a quirk of some AMD drivers on linux, and if otherwise met should be considered a bug. Co-authored-by: Carter Anderson --- crates/bevy_render/src/view/window.rs | 72 ++++++++++++++++++--------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 627c96e8c9..8e6f82c66c 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -220,32 +220,56 @@ pub fn prepare_windows( }, }; - // Do the initial surface configuration if it hasn't been configured yet. Or if size or - // present mode changed. - let frame = if window_surfaces.configured_windows.insert(window.id) - || window.size_changed - || window.present_mode_changed - { - render_device.configure_surface(&surface_data.surface, &surface_configuration); - surface_data - .surface - .get_current_texture() - .expect("Error configuring surface") - } else { - match surface_data.surface.get_current_texture() { - Ok(swap_chain_frame) => swap_chain_frame, - Err(wgpu::SurfaceError::Outdated) => { - render_device.configure_surface(&surface_data.surface, &surface_configuration); - surface_data - .surface - .get_current_texture() - .expect("Error reconfiguring surface") - } - err => err.expect("Failed to acquire next swap chain texture!"), - } + // A recurring issue is hitting `wgpu::SurfaceError::Timeout` on certain Linux + // mesa driver implementations. This seems to be a quirk of some drivers. + // We'd rather keep panicking when not on Linux mesa, because in those case, + // the `Timeout` is still probably the symptom of a degraded unrecoverable + // application state. + // see https://github.com/bevyengine/bevy/pull/5957 + // and https://github.com/gfx-rs/wgpu/issues/1218 + #[cfg(target_os = "linux")] + let may_erroneously_timeout = || { + render_instance + .enumerate_adapters(wgpu::Backends::VULKAN) + .any(|adapter| { + let name = adapter.get_info().name; + name.starts_with("AMD") || name.starts_with("Intel") + }) }; - window.swap_chain_texture = Some(TextureView::from(frame)); + let not_already_configured = window_surfaces.configured_windows.insert(window.id); + + let surface = &surface_data.surface; + if not_already_configured || window.size_changed || window.present_mode_changed { + render_device.configure_surface(surface, &surface_configuration); + let frame = surface + .get_current_texture() + .expect("Error configuring surface"); + window.swap_chain_texture = Some(TextureView::from(frame)); + } else { + match surface.get_current_texture() { + Ok(frame) => { + window.swap_chain_texture = Some(TextureView::from(frame)); + } + Err(wgpu::SurfaceError::Outdated) => { + render_device.configure_surface(surface, &surface_configuration); + let frame = surface + .get_current_texture() + .expect("Error reconfiguring surface"); + window.swap_chain_texture = Some(TextureView::from(frame)); + } + #[cfg(target_os = "linux")] + Err(wgpu::SurfaceError::Timeout) if may_erroneously_timeout() => { + bevy_utils::tracing::trace!( + "Couldn't get swap chain texture. This is probably a quirk \ + of your Linux GPU driver, so it can be safely ignored." + ); + } + Err(err) => { + panic!("Couldn't get swap chain texture, operation unrecoverable: {err}"); + } + } + }; window.swap_chain_texture_format = Some(surface_data.format); } }