From 61bfc96f400f34d2e9c995dd6ce518058336a53e Mon Sep 17 00:00:00 2001 From: Douglas Dwyer Date: Wed, 20 Nov 2024 22:00:24 -0500 Subject: [PATCH] Add dependency on mach-dxcompiler-rs and support statically linking DXC --- Cargo.lock | 10 +++ Cargo.toml | 1 + wgpu-hal/Cargo.toml | 3 + wgpu-hal/src/dx12/instance.rs | 20 ++++- wgpu-hal/src/dx12/shader_compilation.rs | 110 ++++++++++++++++-------- wgpu-types/src/lib.rs | 3 + wgpu/Cargo.toml | 3 + 7 files changed, 110 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ff6df69e..5fd91ca76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,6 +1778,15 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "mach-dxcompiler-rs" +version = "2023.12.14+0b7073b.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7857414cb65eb5f03cf0365ac801f089fdbfbb0c47f3f257941eda735aebdf" +dependencies = [ + "windows-core", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3692,6 +3701,7 @@ dependencies = [ "libc", "libloading", "log", + "mach-dxcompiler-rs", "metal", "naga", "ndk-sys", diff --git a/Cargo.toml b/Cargo.toml index 5e1142575..74cadd8f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,6 +149,7 @@ gpu-descriptor = "0.3" bit-set = "0.8" gpu-allocator = { version = "0.27", default-features = false } range-alloc = "0.1" +mach-dxcompiler-rs = { version = "2023.12.14+0b7073b.1", default-features = false } windows-core = { version = "0.58", default-features = false } # Gles dependencies diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 35e85f45d..60b289455 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -91,6 +91,8 @@ dx12 = [ "windows/Win32_System_Threading", "windows/Win32_UI_WindowsAndMessaging", ] +## Enables the static DXC compiler using the `mach-dxcompiler-rs` crate. +mach-dxcompiler-rs = ["dep:mach-dxcompiler-rs"] renderdoc = ["dep:libloading", "dep:renderdoc-sys"] fragile-send-sync-non-atomic-wasm = ["wgt/fragile-send-sync-non-atomic-wasm"] # Panic when running into an out-of-memory error (for debugging purposes). @@ -158,6 +160,7 @@ windows = { workspace = true, optional = true } bit-set = { workspace = true, optional = true } range-alloc = { workspace = true, optional = true } gpu-allocator = { workspace = true, optional = true } +mach-dxcompiler-rs = { workspace = true, optional = true } # For core macros. This crate is also reexported as windows::core. windows-core = { workspace = true, optional = true } diff --git a/wgpu-hal/src/dx12/instance.rs b/wgpu-hal/src/dx12/instance.rs index 31d0511d3..4d5a78d54 100644 --- a/wgpu-hal/src/dx12/instance.rs +++ b/wgpu-hal/src/dx12/instance.rs @@ -80,10 +80,22 @@ impl crate::Instance for super::Instance { dxil_path, dxc_path, } => { - let container = super::shader_compilation::get_dxc_container(dxc_path, dxil_path) - .map_err(|e| { - crate::InstanceError::with_source(String::from("Failed to load DXC"), e) - })?; + let container = + super::shader_compilation::get_dynamic_dxc_container(dxc_path, dxil_path) + .map_err(|e| { + crate::InstanceError::with_source(String::from("Failed to load DXC"), e) + })?; + + container.map(Arc::new) + } + wgt::Dx12Compiler::MachDxc => { + let container = + super::shader_compilation::get_mach_dxc_container().map_err(|e| { + crate::InstanceError::with_source( + String::from("Failed to load Mach DXC"), + e, + ) + })?; container.map(Arc::new) } diff --git a/wgpu-hal/src/dx12/shader_compilation.rs b/wgpu-hal/src/dx12/shader_compilation.rs index b95ccaa5a..b504a8b25 100644 --- a/wgpu-hal/src/dx12/shader_compilation.rs +++ b/wgpu-hal/src/dx12/shader_compilation.rs @@ -78,6 +78,12 @@ pub(super) fn compile_fxc( } } +type DxcCreateInstanceFn = unsafe extern "system" fn( + rclsid: *const windows_core::GUID, + riid: *const windows_core::GUID, + ppv: *mut *mut core::ffi::c_void, +) -> windows_core::HRESULT; + trait DxcObj: Interface { const CLSID: windows::core::GUID; } @@ -97,7 +103,10 @@ struct DxcLib { } impl DxcLib { - fn new(lib_path: Option, lib_name: &'static str) -> Result { + fn new_dynamic( + lib_path: Option, + lib_name: &'static str, + ) -> Result { let lib_path = if let Some(lib_path) = lib_path { if lib_path.is_file() { lib_path @@ -111,15 +120,19 @@ impl DxcLib { } pub fn create_instance(&self) -> Result { - type Fun = extern "system" fn( - rclsid: *const windows_core::GUID, - riid: *const windows_core::GUID, - ppv: *mut *mut core::ffi::c_void, - ) -> windows_core::HRESULT; - let func: libloading::Symbol = unsafe { self.lib.get(b"DxcCreateInstance\0") }?; + unsafe { + let func: libloading::Symbol = + self.lib.get(b"DxcCreateInstance\0")?; + dxc_create_instance::(*func) + } + } +} +/// Invokes the provided library function to create a DXC object. +unsafe fn dxc_create_instance(f: DxcCreateInstanceFn) -> Result { + unsafe { let mut result__ = None; - (func)(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__)) + f(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__)) .ok() .into_device_result("DxcCreateInstance")?; result__.ok_or(crate::DeviceError::Unexpected) @@ -130,18 +143,20 @@ impl DxcLib { pub(super) struct DxcContainer { compiler: Dxc::IDxcCompiler3, utils: Dxc::IDxcUtils, - validator: Dxc::IDxcValidator, + validator: Option, // Has to be held onto for the lifetime of the device otherwise shaders will fail to compile. - _dxc: DxcLib, + // Only needed when using dynamic linking. + _dxc: Option, // Also Has to be held onto for the lifetime of the device otherwise shaders will fail to validate. - _dxil: DxcLib, + // Only needed when using dynamic linking. + _dxil: Option, } -pub(super) fn get_dxc_container( +pub(super) fn get_dynamic_dxc_container( dxc_path: Option, dxil_path: Option, ) -> Result, crate::DeviceError> { - let dxc = match DxcLib::new(dxc_path, "dxcompiler.dll") { + let dxc = match DxcLib::new_dynamic(dxc_path, "dxcompiler.dll") { Ok(dxc) => dxc, Err(e) => { log::warn!( @@ -153,7 +168,7 @@ pub(super) fn get_dxc_container( } }; - let dxil = match DxcLib::new(dxil_path, "dxil.dll") { + let dxil = match DxcLib::new_dynamic(dxil_path, "dxil.dll") { Ok(dxil) => dxil, Err(e) => { log::warn!( @@ -172,12 +187,37 @@ pub(super) fn get_dxc_container( Ok(Some(DxcContainer { compiler, utils, - validator, - _dxc: dxc, - _dxil: dxil, + validator: Some(validator), + _dxc: Some(dxc), + _dxil: Some(dxil), })) } +/// Creates a [`DxcContainer`] that delegates to the statically-linked `mach-dxcompiler` library. +pub(super) fn get_mach_dxc_container() -> Result, crate::DeviceError> { + #[cfg(feature = "mach-dxcompiler-rs")] + { + unsafe { + let compiler = + dxc_create_instance::(mach_dxcompiler_rs::DxcCreateInstance)?; + let utils = + dxc_create_instance::(mach_dxcompiler_rs::DxcCreateInstance)?; + + Ok(Some(DxcContainer { + compiler, + utils, + validator: None, + _dxc: None, + _dxil: None, + })) + } + } + #[cfg(not(feature = "mach-dxcompiler-rs"))] + { + panic!("Attempted to create a Mach DXC shader compiler, but the mach-dxcompiler-rs feature was not enabled") + } +} + /// Owned PCWSTR #[allow(clippy::upper_case_acronyms)] struct OPCWSTR { @@ -288,26 +328,24 @@ pub(super) fn compile_dxc( let blob = get_output::(&compile_res, Dxc::DXC_OUT_OBJECT)?; - let err_blob = { - let res = unsafe { - dxc_container - .validator - .Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit) + if let Some(validator) = &dxc_container.validator { + let err_blob = { + let res = unsafe { validator.Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit) } + .into_device_result("Validate")?; + + unsafe { res.GetErrorBuffer() }.into_device_result("GetErrorBuffer")? + }; + + let size = unsafe { err_blob.GetBufferSize() }; + if size != 0 { + let err_blob = unsafe { dxc_container.utils.GetBlobAsUtf8(&err_blob) } + .into_device_result("GetBlobAsUtf8")?; + let err = as_err_str(&err_blob)?; + return Err(crate::PipelineError::Linkage( + stage_bit, + format!("DXC validation error: {err}"), + )); } - .into_device_result("Validate")?; - - unsafe { res.GetErrorBuffer() }.into_device_result("GetErrorBuffer")? - }; - - let size = unsafe { err_blob.GetBufferSize() }; - if size != 0 { - let err_blob = unsafe { dxc_container.utils.GetBlobAsUtf8(&err_blob) } - .into_device_result("GetBlobAsUtf8")?; - let err = as_err_str(&err_blob)?; - return Err(crate::PipelineError::Linkage( - stage_bit, - format!("DXC validation error: {err}"), - )); } Ok(crate::dx12::CompiledShader::Dxc(blob)) diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 8053c5723..29d822b5d 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -7401,6 +7401,9 @@ pub enum Dx12Compiler { /// Path to the `dxcompiler.dll` file, or path to the directory containing `dxcompiler.dll` file. Passing `None` will use standard platform specific dll loading rules. dxc_path: Option, }, + /// The statically-linked variant of Dxc, sourced from the [`mach-dxcompiler-rs`](https://crates.io/crates/mach-dxcompiler-rs) crate. + /// The `mach-dxcompiler-rs` feature is required to use this. + MachDxc, } /// Selects which OpenGL ES 3 minor version to request. diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index ed630133e..eafc98aa6 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -73,6 +73,9 @@ glsl = ["naga/glsl-in", "wgc/glsl"] ## Enable accepting WGSL shaders as input. wgsl = ["wgc?/wgsl"] +## Enables the static DXC compiler using the `mach-dxcompiler-rs` crate. +mach-dxcompiler-rs = ["hal/mach-dxcompiler-rs"] + ## Enable accepting naga IR shaders as input. naga-ir = ["dep:naga"]