Skip to content

Fix surface validation errors in WSL2 environments during window resizing #18246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bevy_render/src/renderer/render_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ impl RenderDevice {
/// - A old [`SurfaceTexture`](wgpu::SurfaceTexture) is still alive referencing an old surface.
/// - Texture format requested is unsupported on the surface.
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
// We're now letting the caller decide how to handle errors
surface.configure(&self.device, config);
}

Expand Down
86 changes: 84 additions & 2 deletions crates/bevy_render/src/view/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ use wgpu::{
SurfaceConfiguration, SurfaceTargetUnsafe, TextureFormat, TextureUsages, TextureViewDescriptor,
};

#[cfg(target_os = "linux")]
use std::io::Read;

pub mod screenshot;

use screenshot::{ScreenshotPlugin, ScreenshotToScreenPipeline};
Expand Down Expand Up @@ -193,6 +196,61 @@ impl WindowSurfaces {
}
}

/// Determines if the application is running in a Windows Subsystem for Linux (WSL) environment
#[cfg(target_os = "linux")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: you may want to put this behind a feature flag which is disabled by default for non dev environments. Seems like a small amount of stuff that may not be necessary in production builds, unless someone very specifically wants to compile their binary in a way to be run inside a wsl environment?

fn is_running_in_wsl() -> bool {
// Check for the existence of /proc/sys/kernel/osrelease which should contain "microsoft" or "WSL" if running in WSL
if let Ok(mut file) = std::fs::File::open("/proc/sys/kernel/osrelease") {
let mut content = String::new();
if file.read_to_string(&mut content).is_ok() {
let content_lower = content.to_lowercase();
return content_lower.contains("microsoft") || content_lower.contains("wsl");
}
}
false
}

#[cfg(not(target_os = "linux"))]
fn is_running_in_wsl() -> bool {
false
}

/// Captures a potential panic during surface configuration and logs it instead of crashing
/// This is especially useful for WSL2 environments where surface reconfiguration can fail
fn safely_configure_surface(
render_device: &RenderDevice,
surface: &wgpu::Surface,
config: &SurfaceConfiguration,
is_wsl: bool,
) {
if is_wsl {
// Use a closure to avoid polluting the backtrace with catch_unwind machinery
let result = core::intrinsics::catch_unwind(core::panic::AssertUnwindSafe(|| {
render_device.configure_surface(surface, config);
}));

if let Err(panic_err) = result {
// Log the error but don't crash the application
if let Some(err_str) = panic_err.downcast_ref::<String>() {
warn!("Failed to configure surface in WSL: {}", err_str);
} else if let Some(err_str) = panic_err.downcast_ref::<&str>() {
warn!("Failed to configure surface in WSL: {}", err_str);
} else {
warn!("Failed to configure surface in WSL (unknown error type)");
}

if let Some(err_str) = panic_err.downcast_ref::<String>() {
if err_str.contains("Surface does not support the adapter's queue family") {
warn!("This is a known issue with Vulkan surfaces in WSL2 environments during window resizing");
}
}
}
} else {
// Regular behavior for non-WSL environments
render_device.configure_surface(surface, config);
}
}

/// (re)configures window surfaces, and obtains a swapchain texture for rendering.
///
/// NOTE: `get_current_texture` in `prepare_windows` can take a long time if the GPU workload is
Expand Down Expand Up @@ -220,6 +278,9 @@ pub fn prepare_windows(
render_device: Res<RenderDevice>,
#[cfg(target_os = "linux")] render_instance: Res<RenderInstance>,
) {
// Detect if we're running in WSL
let is_wsl = is_running_in_wsl();

for window in windows.windows.values_mut() {
let window_surfaces = window_surfaces.deref_mut();
let Some(surface_data) = window_surfaces.surfaces.get(&window.entity) else {
Expand Down Expand Up @@ -252,7 +313,14 @@ pub fn prepare_windows(
window.set_swapchain_texture(frame);
}
Err(wgpu::SurfaceError::Outdated) => {
render_device.configure_surface(surface, &surface_data.configuration);
// Use our safe configuration function
safely_configure_surface(
&render_device,
surface,
&surface_data.configuration,
is_wsl,
);

let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(err) => {
Expand All @@ -271,6 +339,10 @@ pub fn prepare_windows(
of your Linux GPU driver, so it can be safely ignored."
);
}
// Handle WSL-specific surface errors
Err(err) if is_wsl => {
warn!("Encountered surface error in WSL environment: {}. This may be due to WSL2 graphics compatibility issues. Graphics may be temporarily distorted but the application will continue.", err);
}
Err(err) => {
panic!("Couldn't get swap chain texture, operation unrecoverable: {err}");
}
Expand Down Expand Up @@ -313,6 +385,14 @@ pub fn create_surfaces(
render_adapter: Res<RenderAdapter>,
render_device: Res<RenderDevice>,
) {
// Detect if we're running in WSL
let is_wsl = is_running_in_wsl();
if is_wsl {
debug!(
"Detected WSL environment - enabling special handling for window surface operations"
);
}

for window in windows.windows.values() {
let data = window_surfaces
.surfaces
Expand Down Expand Up @@ -400,7 +480,9 @@ pub fn create_surfaces(
PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync,
PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync,
};
render_device.configure_surface(&data.surface, &data.configuration);

// Configure surface with special WSL handling if needed
safely_configure_surface(&render_device, &data.surface, &data.configuration, is_wsl);
}

window_surfaces.configured_windows.insert(window.entity);
Expand Down
Loading