Add dependency on mach-dxcompiler-rs and support statically linking DXC

This commit is contained in:
Douglas Dwyer 2024-11-20 22:00:24 -05:00
parent 00a6032eb7
commit 61bfc96f40
7 changed files with 110 additions and 40 deletions

10
Cargo.lock generated
View File

@ -1778,6 +1778,15 @@ dependencies = [
"tracing-subscriber", "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]] [[package]]
name = "malloc_buf" name = "malloc_buf"
version = "0.0.6" version = "0.0.6"
@ -3692,6 +3701,7 @@ dependencies = [
"libc", "libc",
"libloading", "libloading",
"log", "log",
"mach-dxcompiler-rs",
"metal", "metal",
"naga", "naga",
"ndk-sys", "ndk-sys",

View File

@ -149,6 +149,7 @@ gpu-descriptor = "0.3"
bit-set = "0.8" bit-set = "0.8"
gpu-allocator = { version = "0.27", default-features = false } gpu-allocator = { version = "0.27", default-features = false }
range-alloc = "0.1" range-alloc = "0.1"
mach-dxcompiler-rs = { version = "2023.12.14+0b7073b.1", default-features = false }
windows-core = { version = "0.58", default-features = false } windows-core = { version = "0.58", default-features = false }
# Gles dependencies # Gles dependencies

View File

@ -91,6 +91,8 @@ dx12 = [
"windows/Win32_System_Threading", "windows/Win32_System_Threading",
"windows/Win32_UI_WindowsAndMessaging", "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"] renderdoc = ["dep:libloading", "dep:renderdoc-sys"]
fragile-send-sync-non-atomic-wasm = ["wgt/fragile-send-sync-non-atomic-wasm"] 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). # 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 } bit-set = { workspace = true, optional = true }
range-alloc = { workspace = true, optional = true } range-alloc = { workspace = true, optional = true }
gpu-allocator = { 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. # For core macros. This crate is also reexported as windows::core.
windows-core = { workspace = true, optional = true } windows-core = { workspace = true, optional = true }

View File

@ -80,10 +80,22 @@ impl crate::Instance for super::Instance {
dxil_path, dxil_path,
dxc_path, dxc_path,
} => { } => {
let container = super::shader_compilation::get_dxc_container(dxc_path, dxil_path) let container =
.map_err(|e| { super::shader_compilation::get_dynamic_dxc_container(dxc_path, dxil_path)
crate::InstanceError::with_source(String::from("Failed to load DXC"), e) .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) container.map(Arc::new)
} }

View File

@ -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 { trait DxcObj: Interface {
const CLSID: windows::core::GUID; const CLSID: windows::core::GUID;
} }
@ -97,7 +103,10 @@ struct DxcLib {
} }
impl DxcLib { impl DxcLib {
fn new(lib_path: Option<PathBuf>, lib_name: &'static str) -> Result<Self, libloading::Error> { fn new_dynamic(
lib_path: Option<PathBuf>,
lib_name: &'static str,
) -> Result<Self, libloading::Error> {
let lib_path = if let Some(lib_path) = lib_path { let lib_path = if let Some(lib_path) = lib_path {
if lib_path.is_file() { if lib_path.is_file() {
lib_path lib_path
@ -111,15 +120,19 @@ impl DxcLib {
} }
pub fn create_instance<T: DxcObj>(&self) -> Result<T, crate::DeviceError> { pub fn create_instance<T: DxcObj>(&self) -> Result<T, crate::DeviceError> {
type Fun = extern "system" fn( unsafe {
rclsid: *const windows_core::GUID, let func: libloading::Symbol<DxcCreateInstanceFn> =
riid: *const windows_core::GUID, self.lib.get(b"DxcCreateInstance\0")?;
ppv: *mut *mut core::ffi::c_void, dxc_create_instance::<T>(*func)
) -> windows_core::HRESULT; }
let func: libloading::Symbol<Fun> = unsafe { self.lib.get(b"DxcCreateInstance\0") }?; }
}
/// Invokes the provided library function to create a DXC object.
unsafe fn dxc_create_instance<T: DxcObj>(f: DxcCreateInstanceFn) -> Result<T, crate::DeviceError> {
unsafe {
let mut result__ = None; let mut result__ = None;
(func)(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__)) f(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__))
.ok() .ok()
.into_device_result("DxcCreateInstance")?; .into_device_result("DxcCreateInstance")?;
result__.ok_or(crate::DeviceError::Unexpected) result__.ok_or(crate::DeviceError::Unexpected)
@ -130,18 +143,20 @@ impl DxcLib {
pub(super) struct DxcContainer { pub(super) struct DxcContainer {
compiler: Dxc::IDxcCompiler3, compiler: Dxc::IDxcCompiler3,
utils: Dxc::IDxcUtils, utils: Dxc::IDxcUtils,
validator: Dxc::IDxcValidator, validator: Option<Dxc::IDxcValidator>,
// Has to be held onto for the lifetime of the device otherwise shaders will fail to compile. // 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<DxcLib>,
// Also Has to be held onto for the lifetime of the device otherwise shaders will fail to validate. // 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<DxcLib>,
} }
pub(super) fn get_dxc_container( pub(super) fn get_dynamic_dxc_container(
dxc_path: Option<PathBuf>, dxc_path: Option<PathBuf>,
dxil_path: Option<PathBuf>, dxil_path: Option<PathBuf>,
) -> Result<Option<DxcContainer>, crate::DeviceError> { ) -> Result<Option<DxcContainer>, crate::DeviceError> {
let dxc = match DxcLib::new(dxc_path, "dxcompiler.dll") { let dxc = match DxcLib::new_dynamic(dxc_path, "dxcompiler.dll") {
Ok(dxc) => dxc, Ok(dxc) => dxc,
Err(e) => { Err(e) => {
log::warn!( 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, Ok(dxil) => dxil,
Err(e) => { Err(e) => {
log::warn!( log::warn!(
@ -172,12 +187,37 @@ pub(super) fn get_dxc_container(
Ok(Some(DxcContainer { Ok(Some(DxcContainer {
compiler, compiler,
utils, utils,
validator, validator: Some(validator),
_dxc: dxc, _dxc: Some(dxc),
_dxil: dxil, _dxil: Some(dxil),
})) }))
} }
/// Creates a [`DxcContainer`] that delegates to the statically-linked `mach-dxcompiler` library.
pub(super) fn get_mach_dxc_container() -> Result<Option<DxcContainer>, crate::DeviceError> {
#[cfg(feature = "mach-dxcompiler-rs")]
{
unsafe {
let compiler =
dxc_create_instance::<Dxc::IDxcCompiler3>(mach_dxcompiler_rs::DxcCreateInstance)?;
let utils =
dxc_create_instance::<Dxc::IDxcUtils>(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 /// Owned PCWSTR
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
struct OPCWSTR { struct OPCWSTR {
@ -288,26 +328,24 @@ pub(super) fn compile_dxc(
let blob = get_output::<Dxc::IDxcBlob>(&compile_res, Dxc::DXC_OUT_OBJECT)?; let blob = get_output::<Dxc::IDxcBlob>(&compile_res, Dxc::DXC_OUT_OBJECT)?;
let err_blob = { if let Some(validator) = &dxc_container.validator {
let res = unsafe { let err_blob = {
dxc_container let res = unsafe { validator.Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit) }
.validator .into_device_result("Validate")?;
.Validate(&blob, Dxc::DxcValidatorFlags_InPlaceEdit)
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)) Ok(crate::dx12::CompiledShader::Dxc(blob))

View File

@ -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. /// 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<PathBuf>, dxc_path: Option<PathBuf>,
}, },
/// 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. /// Selects which OpenGL ES 3 minor version to request.

View File

@ -73,6 +73,9 @@ glsl = ["naga/glsl-in", "wgc/glsl"]
## Enable accepting WGSL shaders as input. ## Enable accepting WGSL shaders as input.
wgsl = ["wgc?/wgsl"] 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. ## Enable accepting naga IR shaders as input.
naga-ir = ["dep:naga"] naga-ir = ["dep:naga"]