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",
]
[[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",

View File

@ -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

View File

@ -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 }

View File

@ -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)
}

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 {
const CLSID: windows::core::GUID;
}
@ -97,7 +103,10 @@ struct 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 {
if lib_path.is_file() {
lib_path
@ -111,15 +120,19 @@ impl DxcLib {
}
pub fn create_instance<T: DxcObj>(&self) -> Result<T, crate::DeviceError> {
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<Fun> = unsafe { self.lib.get(b"DxcCreateInstance\0") }?;
unsafe {
let func: libloading::Symbol<DxcCreateInstanceFn> =
self.lib.get(b"DxcCreateInstance\0")?;
dxc_create_instance::<T>(*func)
}
}
}
/// 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;
(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<Dxc::IDxcValidator>,
// 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.
_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>,
dxil_path: Option<PathBuf>,
) -> 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,
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<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
#[allow(clippy::upper_case_acronyms)]
struct OPCWSTR {
@ -288,26 +328,24 @@ pub(super) fn compile_dxc(
let blob = get_output::<Dxc::IDxcBlob>(&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))

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.
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.

View File

@ -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"]