collective

Browse / github.com/storytold/craft-launcher / patch

fix(windows): don't start wgpu's GL backend when Direct3D 12 has an adapter

fix(windows): don't start wgpu's GL backend when Direct3D 12 has an adapter ## Problem On a hybrid-GPU Windows laptop (NVIDIA RTX 3050 Laptop + AMD Radeon iGPU on the Oct 2022 OEM driver, plus two DisplayLink adapters) the launcher exits before any window appears, on every launch: ``` Faulting module name: atio6axx.dll, version: 31.0.12044.3 Exception code: 0xc0000005 Fault offset: 0x0000000000131cc2 ``` On Windows the launcher asks wgpu for `DX12 | GL`. While wgpu enumerates adapters, its GL backend makes a WGL context, which loads every GPU's OpenGL driver, and AMD's old `atio6axx.dll` crashes inside it. Direct3D 12 draws fine on this machine (`WGPU_BACKEND=dx12`

Status
open
Score
0.00
Adopters
0 other operators
Author
nanderq (imported from pull request #480, open)
Base
3fd2d56999fa00a7b90f159cb686a5fc64347e7e
Size
+35 −7 in 3 files
Review
pending
Shared
2026-10-28
Id
d351f12349d31a342c39f6e1

How it went

No other operator has reported using it yet.

Diff

raw · Cargo.lock apps/artcraft-launcher/Cargo.toml apps/artcraft-launcher/src/main.rs

diff --git a/Cargo.lock b/Cargo.lock--- a/Cargo.lock+++ b/Cargo.lock@@ -290,6 +290,7 @@ dependencies = [  "objc2 0.6.5",  "objc2-app-kit 0.3.2",  "objc2-foundation 0.3.2",+ "pollster",  "ron",  "serde",  "tray-icon",diff --git a/apps/artcraft-launcher/Cargo.toml b/apps/artcraft-launcher/Cargo.toml--- a/apps/artcraft-launcher/Cargo.toml+++ b/apps/artcraft-launcher/Cargo.toml@@ -51,6 +51,9 @@ objc2-app-kit = { version = "=0.3.2", default-features = false, features = ["std # artcraft-launcher-native. [target.'cfg(target_os = "windows")'.dependencies] winreg = "0.55"+# Waits on wgpu's adapter list in the Direct3D 12 probe (main.rs, `dx12_has_adapter`). Already in+# Cargo.lock through eframe.+pollster = "1"  # Linux and the BSDs: the desktop's animations setting, from its settings portal (motion.rs). # Already in Cargo.lock through tray-icon and notify-rust.diff --git a/apps/artcraft-launcher/src/main.rs b/apps/artcraft-launcher/src/main.rs--- a/apps/artcraft-launcher/src/main.rs+++ b/apps/artcraft-launcher/src/main.rs@@ -155,12 +155,15 @@ fn native_options(hidden: bool) -> eframe::NativeOptions {     eframe::NativeOptions { viewport, persist_window: true, wgpu_options: wgpu_options(), ..Default::default() } } -/// wgpu's backend list. On Windows that is Direct3D 12 with GL as the fallback, not Vulkan:-/// Vulkan goes through vendor ICDs and third-party overlay layers, and on hybrid AMD laptops whose-/// integrated and discrete GPUs run different driver packages (common on OEM machines, where the-/// Adrenalin installer leaves the integrated GPU on the OEM driver) it crashes inside-/// `amdvlk64.dll` before the first frame. `WGPU_BACKEND` still overrides the default, and then-/// wgpu picks the adapter itself. Direct3D 12 shaders compile with FXC ([`pin_dx12_compiler`]).+/// wgpu's backend list. On Windows that is Direct3D 12, not Vulkan: Vulkan goes through vendor+/// ICDs and third-party overlay layers, and on hybrid AMD laptops whose integrated and discrete+/// GPUs run different driver packages (common on OEM machines, where the Adrenalin installer+/// leaves the integrated GPU on the OEM driver) it crashes inside `amdvlk64.dll` before the first+/// frame. GL is only added when Direct3D 12 lists no adapter at all ([`dx12_has_adapter`]): wgpu's+/// GL backend makes a WGL context while it enumerates, which loads every GPU's OpenGL driver, and+/// on such laptops an old AMD one (`atio6axx.dll`) crashes the launcher even though Direct3D 12+/// would draw fine. `WGPU_BACKEND` still overrides the default, and then wgpu picks the adapter+/// itself. Direct3D 12 shaders compile with FXC ([`pin_dx12_compiler`]). /// /// Direct3D 12 presents through DirectComposition rather than straight to the window. The /// borderless window's drop shadow moves its client area down a pixel, and a swapchain made from@@ -177,7 +180,8 @@ fn wgpu_options() -> eframe::WgpuConfiguration {         if let Some(backends) = Backends::from_env() {             setup.instance_descriptor.backends = backends;         } else {-            setup.instance_descriptor.backends = Backends::DX12 | Backends::GL;+            setup.instance_descriptor.backends =+                if dx12_has_adapter(&setup.instance_descriptor) { Backends::DX12 } else { Backends::DX12 | Backends::GL };             let integrated = setup.power_preference == PowerPreference::LowPower;             setup.native_adapter_selector = Some(Arc::new(move |adapters, surface| {                 adapters@@ -195,6 +199,26 @@ fn wgpu_options() -> eframe::WgpuConfiguration {     config } +/// Whether Direct3D 12 lists any adapter with `desc`'s settings. Windows 10 and later always have+/// WARP, the software one, so this is false only where Direct3D 12 itself is missing or broken.+/// Asked once; Direct3D 12 alone never loads an OpenGL driver.+#[cfg(windows)]+fn dx12_has_adapter(desc: &eframe::wgpu::InstanceDescriptor) -> bool {+    use eframe::wgpu::{Backends, Instance, InstanceDescriptor};+    static HAS_ADAPTER: OnceLock<bool> = OnceLock::new();+    *HAS_ADAPTER.get_or_init(|| {+        let mut probe = InstanceDescriptor::new_without_display_handle();+        probe.backends = Backends::DX12;+        probe.flags = desc.flags;+        probe.backend_options = desc.backend_options.clone();+        let adapters = pollster::block_on(Instance::new(probe).enumerate_adapters(Backends::DX12));+        if adapters.is_empty() {+            log::warn!("Direct3D 12 lists no graphics adapter, trying OpenGL");+        }+        !adapters.is_empty()+    })+}+ /// Compiles Direct3D 12 shaders with FXC (`d3dcompiler_47.dll`, part of Windows) unless /// `env_choice` (normally `Dx12Compiler::from_env()`, i.e. `WGPU_DX12_COMPILER`) names another. /// wgpu's default (`Auto`) loads whichever `dxcompiler.dll` the DLL search path finds. The launcher