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 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-05 16:50:45 -04:00
parent 73fdb261ef
commit 0be70c0c8e
1 changed files with 37 additions and 10 deletions

View File

@ -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:?}");
}
}
}