diff --git a/apps/lightcraft/src/window_gpu.rs b/apps/lightcraft/src/window_gpu.rs index 1f84ec477..fefa65a51 100644 --- a/apps/lightcraft/src/window_gpu.rs +++ b/apps/lightcraft/src/window_gpu.rs @@ -93,7 +93,6 @@ pub fn choose(files: &Files) -> Chosen { let compiled = wgpu::Instance::enabled_backend_features(); let os = std::env::consts::OS; let order = backend::window_renderer_order(forced, remembered, failed, os, compiled, true); - let mut notice = failed.map(|f| format!("The window could not start with its {} renderer last time.", f.name())); let mut problems = Vec::new(); let chosen = if cfg!(windows) { pick_probed(&order, &mut problems) @@ -104,18 +103,7 @@ pub fn choose(files: &Files) -> Chosen { Some((WindowRenderer::Wgpu(backend::window_backends()), None)) }; let (which, existing) = chosen.unwrap_or((WindowRenderer::Glow, None)); - if !problems.is_empty() { - notice = Some(format!("{} {}", notice.unwrap_or_default(), problems.join(" ")).trim().to_string()); - } - let notice = notice.map(|n| { - format!( - "{n} {app} now draws its window with {}. To choose another, start {app} with {var}=dx12, vulkan, gl or glow \ - (see docs/gpu-pipeline.md → Troubleshooting).", - which.name(), - app = "LightCraft", - var = "LIGHTCRAFT_GPU_BACKEND", - ) - }); + let notice = user_notice(failed, forced, &problems, which); log::info!("window: renderer {} (order {:?})", which.name(), order.iter().map(|r| r.name()).collect::>()); let mut wgpu_options = base_options(); let renderer = match (which, existing) { @@ -134,6 +122,25 @@ pub fn choose(files: &Files) -> Chosen { Chosen { renderer, wgpu_options, which, notice } } +/// What to tell the user about the window's renderer. A renderer that failed its probe is skipped +/// silently (it is logged, and the one that worked is remembered): the app works, so a pop-up with +/// wgpu's error only alarms (#1624). Shown: the last start died, or the forced renderer failed. +fn user_notice(failed: Option, forced: Option, problems: &[String], which: WindowRenderer) -> Option { + let mut notice = failed.map(|f| format!("The window could not start with its {} renderer last time.", f.name())); + if forced.is_some() && !problems.is_empty() { + notice = Some(format!("{} {}", notice.unwrap_or_default(), problems.join(" ")).trim().to_string()); + } + notice.map(|n| { + format!( + "{n} {app} now draws its window with {}. To choose another, start {app} with {var}=dx12, vulkan, gl or glow \ + (see docs/gpu-pipeline.md → Troubleshooting).", + which.name(), + app = "LightCraft", + var = "LIGHTCRAFT_GPU_BACKEND", + ) + }) +} + /// eframe's defaults with the app's backends and DX12 shader compiler (issues #136, #471). pub fn base_options() -> WgpuConfiguration { let mut c = WgpuConfiguration::default(); @@ -269,6 +276,20 @@ mod tests { let _ = std::fs::remove_dir_all(&d); } + /// #1624: a renderer skipped by the probe (DX12 → Vulkan) is no pop-up; a died start or a + /// failed forced renderer still is. + #[test] + fn probe_fallback_is_silent() { + let (dx, vk) = (WindowRenderer::Wgpu(Backends::DX12), WindowRenderer::Wgpu(Backends::VULKAN)); + let problems = vec!["The dx12 renderer failed (Validation Error).".to_string()]; + assert_eq!(user_notice(None, None, &problems, vk), None); + assert_eq!(user_notice(None, None, &[], vk), None); + let forced = user_notice(None, Some(dx), &problems, vk).unwrap(); + assert!(forced.starts_with("The dx12 renderer failed") && forced.contains("now draws its window with vulkan"), "{forced}"); + let died = user_notice(Some(dx), None, &problems, vk).unwrap(); + assert!(died.contains("last time") && !died.contains("Validation Error"), "{died}"); + } + /// The crash report: a pipeline that fails validation must come back as an error, never as /// wgpu's default panic. Needs an adapter; skipped where there is none (CI without a GPU). #[test] diff --git a/docs/gpu-pipeline.md b/docs/gpu-pipeline.md index ae95d454c..3fb3db297 100644 --- a/docs/gpu-pipeline.md +++ b/docs/gpu-pipeline.md @@ -105,7 +105,8 @@ or worked around with `LIGHTCRAFT_GPU_BACKEND`. **The window's renderer and its fallback (Windows).** Before the window opens, each renderer is probed in order: DX12, Vulkan, GL, then eframe's OpenGL renderer (glow). A probe creates a device and builds egui's pipeline inside wgpu error scopes, so a driver that cannot compile egui's shaders ("The parameter is -incorrect. (0x80070057)" in `egui_pipeline`) is logged and skipped instead of killing the app. The renderer +incorrect. (0x80070057)" in `egui_pipeline`) is logged and skipped instead of killing the app; no pop-up +unless that renderer was forced (#1624). The renderer that worked is remembered in the settings folder (`window-renderer`); a start that dies before the window is up leaves `window-init.marker`, and the next start (relaunched automatically, at most 4 times) tries that renderer last. `LIGHTCRAFT_GPU_BACKEND=dx12 | vulkan | gl | glow` (or `WGPU_BACKEND`) forces one; DX12 stays