From 0be70c0c8ee575a92de440ace1a08a5d3ce41afe Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 5 Oct 2026 16:50:45 -0400 Subject: [PATCH] Issue #37: on Windows, draw with Direct3D 12 (OpenGL fallback) and never load Vulkan drivers wgpu's default backends on Windows include Vulkan, and creating a Vulkan instance loads every installed Vulkan driver into the process. On the reporter's machine Intel's (igvk64.dll) crashed PrintCraft with an access violation before its window appeared; WGPU_BACKEND=dx12 or gl started it normally. PrintCraft now asks for D3D12 with OpenGL as the fallback, unless WGPU_BACKEND says otherwise. The issue #8 low-power preference is kept in the same configure_gpu, and a unit test (run by the Windows CI job) checks Vulkan is left out. Co-Authored-By: Claude Opus 5.5 --- apps/printcraft/src/main.rs | 47 +++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/printcraft/src/main.rs b/apps/printcraft/src/main.rs index 798c2bc6..887fe13e 100644 --- a/apps/printcraft/src/main.rs +++ b/apps/printcraft/src/main.rs @@ -74,7 +74,7 @@ fn main() -> eframe::Result { // eframe would otherwise derive the settings folder from the app id: keep it under "PrintCraft". let persistence_path = eframe::storage_dir("PrintCraft").map(|d| d.join("app.ron")); let mut native = eframe::NativeOptions { viewport, persistence_path, ..Default::default() }; - prefer_integrated_gpu(&mut native); + configure_gpu(&mut native); eframe::run_native( "PrintCraft", native, @@ -130,15 +130,42 @@ fn write_control_file(path: &str, port: u16, token: &str) -> std::io::Result<()> f.write_all(json.as_bytes()) } -/// Draw on the integrated GPU unless `WGPU_POWER_PREF` says otherwise. A PDF viewer has no use -/// for a discrete GPU, and on hybrid-graphics laptops (NVIDIA Optimus) the discrete one can lose -/// or corrupt its memory across suspend and screen lock, leaving the window illegible (issue #8). -/// It also saves battery. Machines with one GPU are unaffected. -fn prefer_integrated_gpu(native: &mut eframe::NativeOptions) { - if std::env::var_os("WGPU_POWER_PREF").is_some() { - return; - } - if let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &mut native.wgpu_options.wgpu_setup { +/// How wgpu finds a GPU. Each choice yields to its wgpu environment variable. +/// +/// - Draw on the integrated GPU unless `WGPU_POWER_PREF` says otherwise. A PDF viewer has no use +/// for a discrete GPU, and on hybrid-graphics laptops (NVIDIA Optimus) the discrete one can lose +/// or corrupt its memory across suspend and screen lock, leaving the window illegible (issue #8). +/// It also saves battery. Machines with one GPU are unaffected. +/// - On Windows, use Direct3D 12, falling back to OpenGL, and never load Vulkan drivers unless +/// `WGPU_BACKEND` asks for them. Creating a Vulkan instance loads every installed Vulkan driver +/// into the process, and a faulty one (an Intel driver in issue #37) crashed PrintCraft before +/// its window appeared. D3D12 is the native, best-supported backend there. +fn configure_gpu(native: &mut eframe::NativeOptions) { + let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &mut native.wgpu_options.wgpu_setup else { return }; + if std::env::var_os("WGPU_POWER_PREF").is_none() { setup.power_preference = eframe::wgpu::PowerPreference::LowPower; } + if cfg!(target_os = "windows") && std::env::var_os("WGPU_BACKEND").is_none() { + setup.instance_descriptor.backends = eframe::wgpu::Backends::DX12 | eframe::wgpu::Backends::GL; + } +} + +#[cfg(test)] +mod tests { + #[test] + fn gpu_backends_avoid_vulkan_on_windows_and_prefer_low_power() { + let mut native = eframe::NativeOptions::default(); + super::configure_gpu(&mut native); + let eframe::egui_wgpu::WgpuSetup::CreateNew(setup) = &native.wgpu_options.wgpu_setup else { + panic!("default setup creates its own instance") + }; + if std::env::var_os("WGPU_POWER_PREF").is_none() { + assert_eq!(setup.power_preference, eframe::wgpu::PowerPreference::LowPower); + } + if cfg!(target_os = "windows") && std::env::var_os("WGPU_BACKEND").is_none() { + let backends = setup.instance_descriptor.backends; + assert!(backends.contains(eframe::wgpu::Backends::DX12), "{backends:?}"); + assert!(!backends.contains(eframe::wgpu::Backends::VULKAN), "issue #37: {backends:?}"); + } + } }