Merge pull request #198 from itrycodinganditsbad/fxc

Windows: compile DX12 shaders with FXC
This commit is contained in:
Brandon Thomas 2026-10-11 20:04:47 -04:00 committed by GitHub
commit f68e6c438a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 57 additions and 7 deletions

View File

@ -277,6 +277,10 @@ Remove-Item Env:WGPU_BACKEND # restore the default for later
An explicit `gl` override can bring the driver crash back on affected systems. The macOS, Linux An explicit `gl` override can bring the driver crash back on affected systems. The macOS, Linux
and web backend defaults are unchanged. and web backend defaults are unchanged.
DirectX 12 shaders are compiled with FXC, which is part of Windows, so no `dxcompiler.dll` or
`dxil.dll` is needed ("Parent device is lost" at start, #185). `$env:WGPU_DX12_COMPILER = "dxc"`
(or `"auto"`) selects wgpu's DXC handling again for troubleshooting.
### macOS ### macOS
| Build | File | Notes | | Build | File | Notes |

View File

@ -1,10 +1,14 @@
//! Windows graphics instance configuration, applied before wgpu can initialize any driver. //! Windows graphics instance configuration, applied before wgpu can initialize any driver.
use eframe::{NativeOptions, egui_wgpu::WgpuSetup, wgpu::Backends}; use eframe::{
NativeOptions,
egui_wgpu::WgpuSetup,
wgpu::{Backends, Dx12Compiler},
};
/// Restrict the wgpu instance to DirectX 12 unless `backend_override` (normally /// Default to DirectX 12 with FXC before creating the wgpu instance. Explicit
/// `Backends::from_env()`, i.e. `WGPU_BACKEND`) names other backends. /// `WGPU_BACKEND` and `WGPU_DX12_COMPILER` choices, read by the caller, take precedence.
pub fn configure(options: &mut NativeOptions, backend_override: Option<Backends>) { pub fn configure(options: &mut NativeOptions, backend_override: Option<Backends>, compiler_override: Option<Dx12Compiler>) {
if let WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup { if let WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup {
// eframe's default includes GL. Creating that backend can crash inside AMD's // eframe's default includes GL. Creating that backend can crash inside AMD's
// atio6axx.dll before adapter selection or any Rust error handling runs, so the // atio6axx.dll before adapter selection or any Rust error handling runs, so the
@ -12,6 +16,16 @@ pub fn configure(options: &mut NativeOptions, backend_override: Option<Backends>
// out of the instance avoids it. This matches the verified WGPU_BACKEND=dx12 // out of the instance avoids it. This matches the verified WGPU_BACKEND=dx12
// workaround without mutating process environment variables. // workaround without mutating process environment variables.
create.instance_descriptor.backends = backend_override.unwrap_or(Backends::DX12); create.instance_descriptor.backends = backend_override.unwrap_or(Backends::DX12);
// Auto loads whichever dxcompiler.dll the DLL search path finds; SoundCraft ships
// none, and without a matching dxil.dll the unsigned-DXIL warning fails wgpu's
// indirect-validation pipeline and loses the device before the first frame
// (issue #185). FXC uses the compiler that is part of Windows.
create.instance_descriptor.backend_options.dx12.shader_compiler = compiler_override.unwrap_or(Dx12Compiler::Fxc);
log::info!(
"Windows graphics: backends={:?}, DX12 compiler={:?}",
create.instance_descriptor.backends,
create.instance_descriptor.backend_options.dx12.shader_compiler
);
} }
} }
@ -21,7 +35,7 @@ mod tests {
fn configured_backends(backend_override: Option<Backends>) -> Backends { fn configured_backends(backend_override: Option<Backends>) -> Backends {
let mut options = NativeOptions::default(); let mut options = NativeOptions::default();
configure(&mut options, backend_override); configure(&mut options, backend_override, None);
let WgpuSetup::CreateNew(create) = options.wgpu_options.wgpu_setup else { let WgpuSetup::CreateNew(create) = options.wgpu_options.wgpu_setup else {
panic!("default native options must create a wgpu instance"); panic!("default native options must create a wgpu instance");
}; };
@ -38,6 +52,38 @@ mod tests {
assert_eq!(configured_backends(None), Backends::DX12); assert_eq!(configured_backends(None), Backends::DX12);
} }
#[test]
fn windows_default_uses_fxc_instead_of_searching_for_dxc() {
let mut options = NativeOptions::default();
if let WgpuSetup::CreateNew(create) = &mut options.wgpu_options.wgpu_setup {
// Auto can find an unrelated DXC without its matching DXIL runtime (issue #185).
create.instance_descriptor.backend_options.dx12.shader_compiler = Dx12Compiler::Auto;
}
configure(&mut options, None, None);
let WgpuSetup::CreateNew(create) = options.wgpu_options.wgpu_setup else {
panic!("default native options must create a wgpu instance");
};
assert!(matches!(create.instance_descriptor.backend_options.dx12.shader_compiler, Dx12Compiler::Fxc));
}
#[test]
fn explicit_compiler_override_is_preserved() {
for compiler in [Dx12Compiler::Fxc, Dx12Compiler::Auto, Dx12Compiler::DynamicDxc { dxc_path: "custom/dxcompiler.dll".into() }] {
let mut options = NativeOptions::default();
configure(&mut options, None, Some(compiler.clone()));
let WgpuSetup::CreateNew(create) = options.wgpu_options.wgpu_setup else {
panic!("default native options must create a wgpu instance");
};
match (compiler, create.instance_descriptor.backend_options.dx12.shader_compiler) {
(Dx12Compiler::Fxc, Dx12Compiler::Fxc) | (Dx12Compiler::Auto, Dx12Compiler::Auto) => {}
(Dx12Compiler::DynamicDxc { dxc_path: expected }, Dx12Compiler::DynamicDxc { dxc_path: actual }) => {
assert_eq!(actual, expected);
}
(expected, actual) => panic!("compiler override changed from {expected:?} to {actual:?}"),
}
}
}
#[test] #[test]
fn explicit_backend_override_is_preserved() { fn explicit_backend_override_is_preserved() {
for backends in [Backends::VULKAN, Backends::GL, Backends::DX12, Backends::VULKAN | Backends::DX12, Backends::empty()] { for backends in [Backends::VULKAN, Backends::GL, Backends::DX12, Backends::VULKAN | Backends::DX12, Backends::empty()] {

View File

@ -225,9 +225,9 @@ fn main() -> eframe::Result {
if let Some(icon) = app_icon() { if let Some(icon) = app_icon() {
options.viewport = options.viewport.with_icon(icon); options.viewport = options.viewport.with_icon(icon);
} }
// Before eframe creates the wgpu instance: default Windows to DirectX 12 only (see graphics.rs). // Before eframe creates the wgpu instance: default Windows to DirectX 12 with FXC (see graphics.rs).
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
graphics::configure(&mut options, eframe::wgpu::Backends::from_env()); graphics::configure(&mut options, eframe::wgpu::Backends::from_env(), eframe::wgpu::Dx12Compiler::from_env());
eframe::run_native( eframe::run_native(
"SoundCraft", "SoundCraft",
options, options,