Skip to content

Commit 4d4a2bf

Browse files
committed
Add dependency on mach-dxcompiler-rs and support statically linking DXC
1 parent 00a6032 commit 4d4a2bf

File tree

7 files changed

+109
-39
lines changed

7 files changed

+109
-39
lines changed

Cargo.lock

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ gpu-descriptor = "0.3"
149149
bit-set = "0.8"
150150
gpu-allocator = { version = "0.27", default-features = false }
151151
range-alloc = "0.1"
152+
mach-dxcompiler-rs = { version = "2023.12.14+0b7073b.1", default-features = false }
152153
windows-core = { version = "0.58", default-features = false }
153154

154155
# Gles dependencies

wgpu-hal/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ dx12 = [
9191
"windows/Win32_System_Threading",
9292
"windows/Win32_UI_WindowsAndMessaging",
9393
]
94+
## Enables the static DXC compiler using the `mach-dxcompiler-rs` crate.
95+
mach-dxcompiler-rs = [ "dep:mach-dxcompiler-rs" ]
9496
renderdoc = ["dep:libloading", "dep:renderdoc-sys"]
9597
fragile-send-sync-non-atomic-wasm = ["wgt/fragile-send-sync-non-atomic-wasm"]
9698
# Panic when running into an out-of-memory error (for debugging purposes).
@@ -158,6 +160,7 @@ windows = { workspace = true, optional = true }
158160
bit-set = { workspace = true, optional = true }
159161
range-alloc = { workspace = true, optional = true }
160162
gpu-allocator = { workspace = true, optional = true }
163+
mach-dxcompiler-rs = { workspace = true, optional = true }
161164
# For core macros. This crate is also reexported as windows::core.
162165
windows-core = { workspace = true, optional = true }
163166

wgpu-hal/src/dx12/instance.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,22 @@ impl crate::Instance for super::Instance {
8080
dxil_path,
8181
dxc_path,
8282
} => {
83-
let container = super::shader_compilation::get_dxc_container(dxc_path, dxil_path)
84-
.map_err(|e| {
85-
crate::InstanceError::with_source(String::from("Failed to load DXC"), e)
86-
})?;
83+
let container =
84+
super::shader_compilation::get_dynamic_dxc_container(dxc_path, dxil_path)
85+
.map_err(|e| {
86+
crate::InstanceError::with_source(String::from("Failed to load DXC"), e)
87+
})?;
88+
89+
container.map(Arc::new)
90+
}
91+
wgt::Dx12Compiler::MachDxc => {
92+
let container =
93+
super::shader_compilation::get_mach_dxc_container().map_err(|e| {
94+
crate::InstanceError::with_source(
95+
String::from("Failed to load Mach DXC"),
96+
e,
97+
)
98+
})?;
8799

88100
container.map(Arc::new)
89101
}

wgpu-hal/src/dx12/shader_compilation.rs

+73-35
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ pub(super) fn compile_fxc(
7878
}
7979
}
8080

81+
type DxcCreateInstanceFn = unsafe extern "system" fn(
82+
rclsid: *const windows_core::GUID,
83+
riid: *const windows_core::GUID,
84+
ppv: *mut *mut core::ffi::c_void,
85+
) -> windows_core::HRESULT;
86+
8187
trait DxcObj: Interface {
8288
const CLSID: windows::core::GUID;
8389
}
@@ -97,7 +103,10 @@ struct DxcLib {
97103
}
98104

99105
impl DxcLib {
100-
fn new(lib_path: Option<PathBuf>, lib_name: &'static str) -> Result<Self, libloading::Error> {
106+
fn new_dynamic(
107+
lib_path: Option<PathBuf>,
108+
lib_name: &'static str,
109+
) -> Result<Self, libloading::Error> {
101110
let lib_path = if let Some(lib_path) = lib_path {
102111
if lib_path.is_file() {
103112
lib_path
@@ -111,15 +120,19 @@ impl DxcLib {
111120
}
112121

113122
pub fn create_instance<T: DxcObj>(&self) -> Result<T, crate::DeviceError> {
114-
type Fun = extern "system" fn(
115-
rclsid: *const windows_core::GUID,
116-
riid: *const windows_core::GUID,
117-
ppv: *mut *mut core::ffi::c_void,
118-
) -> windows_core::HRESULT;
119-
let func: libloading::Symbol<Fun> = unsafe { self.lib.get(b"DxcCreateInstance\0") }?;
123+
unsafe {
124+
let func: libloading::Symbol<DxcCreateInstanceFn> =
125+
self.lib.get(b"DxcCreateInstance\0")?;
126+
dxc_create_instance::<T>(*func)
127+
}
128+
}
129+
}
120130

131+
/// Invokes the provided library function to create a DXC object.
132+
unsafe fn dxc_create_instance<T: DxcObj>(f: DxcCreateInstanceFn) -> Result<T, crate::DeviceError> {
133+
unsafe {
121134
let mut result__ = None;
122-
(func)(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__))
135+
f(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__))
123136
.ok()
124137
.into_device_result("DxcCreateInstance")?;
125138
result__.ok_or(crate::DeviceError::Unexpected)
@@ -130,18 +143,20 @@ impl DxcLib {
130143
pub(super) struct DxcContainer {
131144
compiler: Dxc::IDxcCompiler3,
132145
utils: Dxc::IDxcUtils,
133-
validator: Dxc::IDxcValidator,
146+
validator: Option<Dxc::IDxcValidator>,
134147
// Has to be held onto for the lifetime of the device otherwise shaders will fail to compile.
135-
_dxc: DxcLib,
148+
// Only needed when using dynamic linking.
149+
_dxc: Option<DxcLib>,
136150
// Also Has to be held onto for the lifetime of the device otherwise shaders will fail to validate.
137-
_dxil: DxcLib,
151+
// Only needed when using dynamic linking.
152+
_dxil: Option<DxcLib>,
138153
}
139154

140-
pub(super) fn get_dxc_container(
155+
pub(super) fn get_dynamic_dxc_container(
141156
dxc_path: Option<PathBuf>,
142157
dxil_path: Option<PathBuf>,
143158
) -> Result<Option<DxcContainer>, crate::DeviceError> {
144-
let dxc = match DxcLib::new(dxc_path, "dxcompiler.dll") {
159+
let dxc = match DxcLib::new_dynamic(dxc_path, "dxcompiler.dll") {
145160
Ok(dxc) => dxc,
146161
Err(e) => {
147162
log::warn!(
@@ -153,7 +168,7 @@ pub(super) fn get_dxc_container(
153168
}
154169
};
155170

156-
let dxil = match DxcLib::new(dxil_path, "dxil.dll") {
171+
let dxil = match DxcLib::new_dynamic(dxil_path, "dxil.dll") {
157172
Ok(dxil) => dxil,
158173
Err(e) => {
159174
log::warn!(
@@ -172,12 +187,37 @@ pub(super) fn get_dxc_container(
172187
Ok(Some(DxcContainer {
173188
compiler,
174189
utils,
175-
validator,
176-
_dxc: dxc,
177-
_dxil: dxil,
190+
validator: Some(validator),
191+
_dxc: Some(dxc),
192+
_dxil: Some(dxil),
178193
}))
179194
}
180195

196+
/// Creates a [`DxcContainer`] that delegates to the statically-linked `mach-dxcompiler` library.
197+
pub(super) fn get_mach_dxc_container() -> Result<Option<DxcContainer>, crate::DeviceError> {
198+
#[cfg(feature = "mach-dxcompiler-rs")]
199+
{
200+
unsafe {
201+
let compiler =
202+
dxc_create_instance::<Dxc::IDxcCompiler3>(mach_dxcompiler_rs::DxcCreateInstance)?;
203+
let utils =
204+
dxc_create_instance::<Dxc::IDxcUtils>(mach_dxcompiler_rs::DxcCreateInstance)?;
205+
206+
Ok(Some(DxcContainer {
207+
compiler,
208+
utils,
209+
validator: None,
210+
_dxc: None,
211+
_dxil: None,
212+
}))
213+
}
214+
}
215+
#[cfg(not(feature = "mach-dxcompiler-rs"))]
216+
{
217+
panic!("Attempted to create a Mach DXC shader compiler, but the mach-dxcompiler-rs feature was not enabled")
218+
}
219+
}
220+
181221
/// Owned PCWSTR
182222
#[allow(clippy::upper_case_acronyms)]
183223
struct OPCWSTR {
@@ -288,26 +328,24 @@ pub(super) fn compile_dxc(
288328

289329
let blob = get_output::<Dxc::IDxcBlob>(&compile_res, Dxc::DXC_OUT_OBJECT)?;
290330

291-
let err_blob = {
292-
let res = unsafe {
293-
dxc_container
294-
.validator
295-
.Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit)
296-
}
297-
.into_device_result("Validate")?;
331+
if let Some(validator) = &dxc_container.validator {
332+
let err_blob = {
333+
let res = unsafe { validator.Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit) }
334+
.into_device_result("Validate")?;
298335

299-
unsafe { res.GetErrorBuffer() }.into_device_result("GetErrorBuffer")?
300-
};
336+
unsafe { res.GetErrorBuffer() }.into_device_result("GetErrorBuffer")?
337+
};
301338

302-
let size = unsafe { err_blob.GetBufferSize() };
303-
if size != 0 {
304-
let err_blob = unsafe { dxc_container.utils.GetBlobAsUtf8(&err_blob) }
305-
.into_device_result("GetBlobAsUtf8")?;
306-
let err = as_err_str(&err_blob)?;
307-
return Err(crate::PipelineError::Linkage(
308-
stage_bit,
309-
format!("DXC validation error: {err}"),
310-
));
339+
let size = unsafe { err_blob.GetBufferSize() };
340+
if size != 0 {
341+
let err_blob = unsafe { dxc_container.utils.GetBlobAsUtf8(&err_blob) }
342+
.into_device_result("GetBlobAsUtf8")?;
343+
let err = as_err_str(&err_blob)?;
344+
return Err(crate::PipelineError::Linkage(
345+
stage_bit,
346+
format!("DXC validation error: {err}"),
347+
));
348+
}
311349
}
312350

313351
Ok(crate::dx12::CompiledShader::Dxc(blob))

wgpu-types/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -7401,6 +7401,9 @@ pub enum Dx12Compiler {
74017401
/// 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.
74027402
dxc_path: Option<PathBuf>,
74037403
},
7404+
/// The statically-linked variant of Dxc, sourced from the [`mach-dxcompiler-rs`](https://crates.io/crates/mach-dxcompiler-rs) crate.
7405+
/// The `mach-dxcompiler-rs` feature is required to use this.
7406+
MachDxc,
74047407
}
74057408

74067409
/// Selects which OpenGL ES 3 minor version to request.

wgpu/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ glsl = ["naga/glsl-in", "wgc/glsl"]
7373
## Enable accepting WGSL shaders as input.
7474
wgsl = ["wgc?/wgsl"]
7575

76+
## Enables the static DXC compiler using the `mach-dxcompiler-rs` crate.
77+
mach-dxcompiler-rs = ["hal/mach-dxcompiler-rs"]
78+
7679
## Enable accepting naga IR shaders as input.
7780
naga-ir = ["dep:naga"]
7881

0 commit comments

Comments
 (0)