mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-21 22:33:49 +00:00
[d3d12] use windows-rs's bindings for DXC instead of hassle-rs
This commit is contained in:
parent
912bdcd336
commit
9d24888848
116
Cargo.lock
generated
116
Cargo.lock
generated
@ -192,7 +192,7 @@ dependencies = [
|
||||
"argh_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -242,7 +242,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -385,7 +385,7 @@ checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -528,7 +528,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -559,37 +559,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "com"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6"
|
||||
dependencies = [
|
||||
"com_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "com_macros"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5"
|
||||
dependencies = [
|
||||
"com_macros_support",
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "com_macros_support"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@ -770,7 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -867,7 +836,7 @@ dependencies = [
|
||||
"quote",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@ -940,7 +909,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -953,7 +922,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version 0.4.0",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1027,7 +996,7 @@ checksum = "b36f2ddfca91251bed7f931f24b192e4eaf0a0e0fa70cf81cfb1416a1973620e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1148,7 +1117,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1245,7 +1214,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1481,21 +1450,6 @@ dependencies = [
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hassle-rs"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"com",
|
||||
"libc",
|
||||
"libloading",
|
||||
"thiserror",
|
||||
"widestring",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
@ -2084,7 +2038,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2276,7 +2230,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2405,7 +2359,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f"
|
||||
dependencies = [
|
||||
"proc-macro-rules-macros",
|
||||
"proc-macro2",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2417,7 +2371,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2740,7 +2694,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2958,18 +2912,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.75",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3009,7 +2952,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3117,7 +3060,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3410,7 +3353,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -3444,7 +3387,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -3478,7 +3421,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3730,7 +3673,6 @@ dependencies = [
|
||||
"gpu-alloc",
|
||||
"gpu-allocator",
|
||||
"gpu-descriptor",
|
||||
"hassle-rs",
|
||||
"js-sys",
|
||||
"khronos-egl",
|
||||
"libc",
|
||||
@ -3777,7 +3719,7 @@ version = "22.0.0"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3839,12 +3781,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -3907,7 +3843,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3918,7 +3854,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4292,5 +4228,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.75",
|
||||
"syn",
|
||||
]
|
||||
|
@ -149,7 +149,6 @@ gpu-descriptor = "0.3"
|
||||
bit-set = "0.8"
|
||||
gpu-allocator = { version = "0.27", default-features = false }
|
||||
range-alloc = "0.1"
|
||||
hassle-rs = "0.11.0"
|
||||
windows-core = { version = "0.58", default-features = false }
|
||||
|
||||
# Gles dependencies
|
||||
|
@ -78,6 +78,7 @@ dx12 = [
|
||||
"gpu-allocator/d3d12",
|
||||
"naga/hlsl-out-if-target-windows",
|
||||
"windows/Win32_Graphics_Direct3D_Fxc",
|
||||
"windows/Win32_Graphics_Direct3D_Dxc",
|
||||
"windows/Win32_Graphics_Direct3D",
|
||||
"windows/Win32_Graphics_Direct3D12",
|
||||
"windows/Win32_Graphics_DirectComposition",
|
||||
@ -89,7 +90,6 @@ dx12 = [
|
||||
"windows/Win32_System_Threading",
|
||||
"windows/Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
dxc_shader_compiler = ["dep:hassle-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).
|
||||
@ -156,7 +156,6 @@ windows = { workspace = true, optional = true }
|
||||
bit-set = { workspace = true, optional = true }
|
||||
range-alloc = { workspace = true, optional = true }
|
||||
gpu-allocator = { workspace = true, optional = true }
|
||||
hassle-rs = { workspace = true, optional = true }
|
||||
# For core macros. This crate is also reexported as windows::core.
|
||||
windows-core = { workspace = true, optional = true }
|
||||
|
||||
|
@ -288,7 +288,7 @@ impl super::Device {
|
||||
};
|
||||
|
||||
let full_stage = format!(
|
||||
"{}_{}\0",
|
||||
"{}_{}",
|
||||
naga_stage.to_hlsl_str(),
|
||||
naga_options.shader_model.to_str()
|
||||
);
|
||||
@ -306,28 +306,33 @@ impl super::Device {
|
||||
let source_name = stage.module.raw_name.as_deref();
|
||||
|
||||
// Compile with DXC if available, otherwise fall back to FXC
|
||||
let (result, log_level) = if let Some(ref dxc_container) = self.dxc_container {
|
||||
let result = if let Some(ref dxc_container) = self.dxc_container {
|
||||
shader_compilation::compile_dxc(
|
||||
self,
|
||||
&source,
|
||||
source_name,
|
||||
raw_ep,
|
||||
stage_bit,
|
||||
full_stage,
|
||||
&full_stage,
|
||||
dxc_container,
|
||||
)
|
||||
} else {
|
||||
let full_stage = ffi::CStr::from_bytes_with_nul(full_stage.as_bytes()).unwrap();
|
||||
shader_compilation::compile_fxc(
|
||||
self,
|
||||
&source,
|
||||
source_name,
|
||||
&ffi::CString::new(raw_ep.as_str()).unwrap(),
|
||||
raw_ep,
|
||||
stage_bit,
|
||||
full_stage,
|
||||
&full_stage,
|
||||
)
|
||||
};
|
||||
|
||||
let log_level = if result.is_ok() {
|
||||
log::Level::Info
|
||||
} else {
|
||||
log::Level::Error
|
||||
};
|
||||
|
||||
log::log!(
|
||||
log_level,
|
||||
"Naga generated shader for {:?} at {:?}:\n{}",
|
||||
|
@ -71,7 +71,10 @@ struct DynLib {
|
||||
}
|
||||
|
||||
impl DynLib {
|
||||
unsafe fn new(filename: &'static str) -> Result<Self, libloading::Error> {
|
||||
unsafe fn new<P>(filename: P) -> Result<Self, libloading::Error>
|
||||
where
|
||||
P: AsRef<ffi::OsStr>,
|
||||
{
|
||||
unsafe { libloading::Library::new(filename) }.map(|inner| Self { inner })
|
||||
}
|
||||
|
||||
@ -919,8 +922,7 @@ pub struct ShaderModule {
|
||||
impl crate::DynShaderModule for ShaderModule {}
|
||||
|
||||
pub(super) enum CompiledShader {
|
||||
#[allow(unused)]
|
||||
Dxc(Vec<u8>),
|
||||
Dxc(Direct3D::Dxc::IDxcBlob),
|
||||
Fxc(Direct3D::ID3DBlob),
|
||||
}
|
||||
|
||||
@ -928,8 +930,8 @@ impl CompiledShader {
|
||||
fn create_native_shader(&self) -> Direct3D12::D3D12_SHADER_BYTECODE {
|
||||
match self {
|
||||
CompiledShader::Dxc(shader) => Direct3D12::D3D12_SHADER_BYTECODE {
|
||||
pShaderBytecode: shader.as_ptr().cast(),
|
||||
BytecodeLength: shader.len(),
|
||||
pShaderBytecode: unsafe { shader.GetBufferPointer() },
|
||||
BytecodeLength: unsafe { shader.GetBufferSize() },
|
||||
},
|
||||
CompiledShader::Fxc(shader) => Direct3D12::D3D12_SHADER_BYTECODE {
|
||||
pShaderBytecode: unsafe { shader.GetBufferPointer() },
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::auxil::dxgi::result::HResult;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::path::PathBuf;
|
||||
use windows::{
|
||||
core::{Interface, PCSTR, PCWSTR},
|
||||
Win32::Graphics::Direct3D::{Dxc, Fxc},
|
||||
};
|
||||
|
||||
pub(super) use dxc::{compile_dxc, get_dxc_container, DxcContainer};
|
||||
use windows::Win32::Graphics::Direct3D;
|
||||
|
||||
// This exists so that users who don't want to use dxc can disable the dxc_shader_compiler feature
|
||||
// and not have to compile hassle_rs.
|
||||
// Currently this will use Dxc if it is chosen as the dx12 compiler at `Instance` creation time, and will
|
||||
// fallback to FXC if the Dxc libraries (dxil.dll and dxcompiler.dll) are not found, or if Fxc is chosen at'
|
||||
// `Instance` creation time.
|
||||
@ -14,40 +14,41 @@ pub(super) fn compile_fxc(
|
||||
device: &super::Device,
|
||||
source: &str,
|
||||
source_name: Option<&CStr>,
|
||||
raw_ep: &CStr,
|
||||
raw_ep: &str,
|
||||
stage_bit: wgt::ShaderStages,
|
||||
full_stage: &CStr,
|
||||
) -> (
|
||||
Result<super::CompiledShader, crate::PipelineError>,
|
||||
log::Level,
|
||||
) {
|
||||
full_stage: &str,
|
||||
) -> Result<super::CompiledShader, crate::PipelineError> {
|
||||
profiling::scope!("compile_fxc");
|
||||
let mut shader_data = None;
|
||||
let mut compile_flags = Direct3D::Fxc::D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
let mut compile_flags = Fxc::D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
if device
|
||||
.private_caps
|
||||
.instance_flags
|
||||
.contains(wgt::InstanceFlags::DEBUG)
|
||||
{
|
||||
compile_flags |=
|
||||
Direct3D::Fxc::D3DCOMPILE_DEBUG | Direct3D::Fxc::D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
compile_flags |= Fxc::D3DCOMPILE_DEBUG | Fxc::D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
}
|
||||
|
||||
let raw_ep = std::ffi::CString::new(raw_ep).unwrap();
|
||||
let full_stage = std::ffi::CString::new(full_stage).unwrap();
|
||||
|
||||
// If no name has been set, D3DCompile wants the null pointer.
|
||||
let source_name = source_name.map(|cstr| cstr.as_ptr()).unwrap_or(ptr::null());
|
||||
let source_name = source_name
|
||||
.map(|cstr| cstr.as_ptr().cast())
|
||||
.unwrap_or(core::ptr::null());
|
||||
|
||||
let mut error = None;
|
||||
let hr = unsafe {
|
||||
profiling::scope!("Direct3D::Fxc::D3DCompile");
|
||||
Direct3D::Fxc::D3DCompile(
|
||||
profiling::scope!("Fxc::D3DCompile");
|
||||
Fxc::D3DCompile(
|
||||
// TODO: Update low-level bindings to accept a slice here
|
||||
source.as_ptr().cast(),
|
||||
source.len(),
|
||||
windows::core::PCSTR(source_name.cast()),
|
||||
PCSTR(source_name),
|
||||
None,
|
||||
None,
|
||||
windows::core::PCSTR(raw_ep.as_ptr().cast()),
|
||||
windows::core::PCSTR(full_stage.as_ptr().cast()),
|
||||
PCSTR(raw_ep.as_ptr().cast()),
|
||||
PCSTR(full_stage.as_ptr().cast()),
|
||||
compile_flags,
|
||||
0,
|
||||
&mut shader_data,
|
||||
@ -58,10 +59,7 @@ pub(super) fn compile_fxc(
|
||||
match hr {
|
||||
Ok(()) => {
|
||||
let shader_data = shader_data.unwrap();
|
||||
(
|
||||
Ok(super::CompiledShader::Fxc(shader_data)),
|
||||
log::Level::Info,
|
||||
)
|
||||
Ok(super::CompiledShader::Fxc(shader_data))
|
||||
}
|
||||
Err(e) => {
|
||||
let mut full_msg = format!("FXC D3DCompile error ({e})");
|
||||
@ -75,48 +73,75 @@ pub(super) fn compile_fxc(
|
||||
};
|
||||
let _ = write!(full_msg, ": {}", String::from_utf8_lossy(message));
|
||||
}
|
||||
(
|
||||
Err(crate::PipelineError::Linkage(stage_bit, full_msg)),
|
||||
log::Level::Warn,
|
||||
)
|
||||
Err(crate::PipelineError::Linkage(stage_bit, full_msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Dxc implementation is behind a feature flag so that users who don't want to use dxc can disable the feature.
|
||||
#[cfg(feature = "dxc_shader_compiler")]
|
||||
mod dxc {
|
||||
use std::ffi::CStr;
|
||||
use std::path::PathBuf;
|
||||
trait DxcObj: Interface {
|
||||
const CLSID: windows::core::GUID;
|
||||
}
|
||||
impl DxcObj for Dxc::IDxcCompiler3 {
|
||||
const CLSID: windows::core::GUID = Dxc::CLSID_DxcCompiler;
|
||||
}
|
||||
impl DxcObj for Dxc::IDxcUtils {
|
||||
const CLSID: windows::core::GUID = Dxc::CLSID_DxcUtils;
|
||||
}
|
||||
impl DxcObj for Dxc::IDxcValidator {
|
||||
const CLSID: windows::core::GUID = Dxc::CLSID_DxcValidator;
|
||||
}
|
||||
|
||||
// Destructor order should be fine since _dxil and _dxc don't rely on each other.
|
||||
pub(crate) struct DxcContainer {
|
||||
compiler: hassle_rs::DxcCompiler,
|
||||
library: hassle_rs::DxcLibrary,
|
||||
validator: hassle_rs::DxcValidator,
|
||||
// Has to be held onto for the lifetime of the device otherwise shaders will fail to compile.
|
||||
_dxc: hassle_rs::Dxc,
|
||||
// Also Has to be held onto for the lifetime of the device otherwise shaders will fail to validate.
|
||||
_dxil: hassle_rs::Dxil,
|
||||
#[derive(Debug)]
|
||||
struct DxcLib {
|
||||
lib: crate::dx12::DynLib,
|
||||
}
|
||||
|
||||
impl DxcLib {
|
||||
fn new(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
|
||||
} else {
|
||||
lib_path.join(lib_name)
|
||||
}
|
||||
} else {
|
||||
PathBuf::from(lib_name)
|
||||
};
|
||||
unsafe { crate::dx12::DynLib::new(lib_path).map(|lib| Self { lib }) }
|
||||
}
|
||||
|
||||
pub(crate) fn get_dxc_container(
|
||||
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") }?;
|
||||
|
||||
let mut result__ = None;
|
||||
(func)(&T::CLSID, &T::IID, <*mut _>::cast(&mut result__))
|
||||
.ok()
|
||||
.into_device_result("DxcCreateInstance")?;
|
||||
result__.ok_or(crate::DeviceError::Unexpected)
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor order should be fine since _dxil and _dxc don't rely on each other.
|
||||
pub(super) struct DxcContainer {
|
||||
compiler: Dxc::IDxcCompiler3,
|
||||
utils: Dxc::IDxcUtils,
|
||||
validator: Dxc::IDxcValidator,
|
||||
// Has to be held onto for the lifetime of the device otherwise shaders will fail to compile.
|
||||
_dxc: DxcLib,
|
||||
// Also Has to be held onto for the lifetime of the device otherwise shaders will fail to validate.
|
||||
_dxil: DxcLib,
|
||||
}
|
||||
|
||||
pub(super) fn get_dxc_container(
|
||||
dxc_path: Option<PathBuf>,
|
||||
dxil_path: Option<PathBuf>,
|
||||
) -> Result<Option<DxcContainer>, crate::DeviceError> {
|
||||
// Make sure that dxil.dll exists.
|
||||
let dxil = match hassle_rs::Dxil::new(dxil_path) {
|
||||
Ok(dxil) => dxil,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load dxil.dll. Defaulting to FXC instead: {}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// Needed for explicit validation.
|
||||
let validator = dxil.create_validator()?;
|
||||
|
||||
let dxc = match hassle_rs::Dxc::new(dxc_path) {
|
||||
) -> Result<Option<DxcContainer>, crate::DeviceError> {
|
||||
let dxc = match DxcLib::new(dxc_path, "dxcompiler.dll") {
|
||||
Ok(dxc) => dxc,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
@ -126,183 +151,159 @@ mod dxc {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
let compiler = dxc.create_compiler()?;
|
||||
let library = dxc.create_library()?;
|
||||
|
||||
let dxil = match DxcLib::new(dxil_path, "dxil.dll") {
|
||||
Ok(dxil) => dxil,
|
||||
Err(e) => {
|
||||
log::warn!("Failed to load dxil.dll. Defaulting to FXC instead: {}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
let compiler = dxc.create_instance::<Dxc::IDxcCompiler3>()?;
|
||||
let utils = dxc.create_instance::<Dxc::IDxcUtils>()?;
|
||||
let validator = dxil.create_instance::<Dxc::IDxcValidator>()?;
|
||||
|
||||
Ok(Some(DxcContainer {
|
||||
_dxc: dxc,
|
||||
compiler,
|
||||
library,
|
||||
_dxil: dxil,
|
||||
utils,
|
||||
validator,
|
||||
_dxc: dxc,
|
||||
_dxil: dxil,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Owned PCWSTR
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
struct OPCWSTR {
|
||||
inner: Vec<u16>,
|
||||
}
|
||||
|
||||
impl OPCWSTR {
|
||||
fn new(s: &str) -> Self {
|
||||
let mut inner: Vec<_> = s.encode_utf16().collect();
|
||||
inner.push(0);
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub(crate) fn compile_dxc(
|
||||
fn ptr(&self) -> PCWSTR {
|
||||
PCWSTR(self.inner.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_output<T: Interface>(
|
||||
res: &Dxc::IDxcResult,
|
||||
kind: Dxc::DXC_OUT_KIND,
|
||||
) -> Result<T, crate::DeviceError> {
|
||||
let mut result__: Option<T> = None;
|
||||
unsafe { res.GetOutput::<T>(kind, &mut None, <*mut _>::cast(&mut result__)) }
|
||||
.into_device_result("GetOutput")?;
|
||||
result__.ok_or(crate::DeviceError::Unexpected)
|
||||
}
|
||||
|
||||
fn as_err_str(blob: &Dxc::IDxcBlobUtf8) -> Result<&str, crate::DeviceError> {
|
||||
let ptr = unsafe { blob.GetStringPointer() };
|
||||
let len = unsafe { blob.GetStringLength() };
|
||||
core::str::from_utf8(unsafe { core::slice::from_raw_parts(ptr.0, len) })
|
||||
.map_err(|_| crate::DeviceError::Unexpected)
|
||||
}
|
||||
|
||||
pub(super) fn compile_dxc(
|
||||
device: &crate::dx12::Device,
|
||||
source: &str,
|
||||
source_name: Option<&CStr>,
|
||||
raw_ep: &str,
|
||||
stage_bit: wgt::ShaderStages,
|
||||
full_stage: String,
|
||||
full_stage: &str,
|
||||
dxc_container: &DxcContainer,
|
||||
) -> (
|
||||
Result<crate::dx12::CompiledShader, crate::PipelineError>,
|
||||
log::Level,
|
||||
) {
|
||||
) -> Result<crate::dx12::CompiledShader, crate::PipelineError> {
|
||||
profiling::scope!("compile_dxc");
|
||||
let mut compile_flags = arrayvec::ArrayVec::<&str, 6>::new_const();
|
||||
compile_flags.push("-Ges"); // Direct3D::Fxc::D3DCOMPILE_ENABLE_STRICTNESS
|
||||
compile_flags.push("-Vd"); // Disable implicit validation to work around bugs when dxil.dll isn't in the local directory.
|
||||
compile_flags.push("-HV"); // Use HLSL 2018, Naga doesn't supported 2021 yet.
|
||||
compile_flags.push("2018");
|
||||
|
||||
let source_name = source_name.and_then(|cstr| cstr.to_str().ok());
|
||||
|
||||
let source_name = source_name.map(OPCWSTR::new);
|
||||
let raw_ep = OPCWSTR::new(raw_ep);
|
||||
let full_stage = OPCWSTR::new(full_stage);
|
||||
|
||||
let mut compile_args = arrayvec::ArrayVec::<PCWSTR, 12>::new_const();
|
||||
|
||||
if let Some(source_name) = source_name.as_ref() {
|
||||
compile_args.push(source_name.ptr())
|
||||
}
|
||||
|
||||
compile_args.extend([
|
||||
windows::core::w!("-E"),
|
||||
raw_ep.ptr(),
|
||||
windows::core::w!("-T"),
|
||||
full_stage.ptr(),
|
||||
windows::core::w!("-HV"),
|
||||
windows::core::w!("2018"), // Use HLSL 2018, Naga doesn't supported 2021 yet.
|
||||
windows::core::w!("-no-warnings"),
|
||||
Dxc::DXC_ARG_ENABLE_STRICTNESS,
|
||||
Dxc::DXC_ARG_SKIP_VALIDATION, // Disable implicit validation to work around bugs when dxil.dll isn't in the local directory.
|
||||
]);
|
||||
|
||||
if device
|
||||
.private_caps
|
||||
.instance_flags
|
||||
.contains(wgt::InstanceFlags::DEBUG)
|
||||
{
|
||||
compile_flags.push("-Zi"); // Direct3D::Fxc::D3DCOMPILE_SKIP_OPTIMIZATION
|
||||
compile_flags.push("-Od"); // Direct3D::Fxc::D3DCOMPILE_DEBUG
|
||||
compile_args.push(Dxc::DXC_ARG_DEBUG);
|
||||
compile_args.push(Dxc::DXC_ARG_SKIP_OPTIMIZATIONS);
|
||||
}
|
||||
|
||||
let blob = match dxc_container
|
||||
.library
|
||||
.create_blob_with_encoding_from_str(source)
|
||||
.map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("DXC blob error: {e}")))
|
||||
{
|
||||
Ok(blob) => blob,
|
||||
Err(e) => return (Err(e), log::Level::Error),
|
||||
let buffer = Dxc::DxcBuffer {
|
||||
Ptr: source.as_ptr().cast(),
|
||||
Size: source.len(),
|
||||
Encoding: Dxc::DXC_CP_UTF8.0,
|
||||
};
|
||||
|
||||
let source_name = source_name
|
||||
.and_then(|cstr| cstr.to_str().ok())
|
||||
.unwrap_or("");
|
||||
|
||||
let compiled = dxc_container.compiler.compile(
|
||||
&blob,
|
||||
source_name,
|
||||
raw_ep,
|
||||
&full_stage,
|
||||
&compile_flags,
|
||||
None,
|
||||
&[],
|
||||
);
|
||||
|
||||
let (result, log_level) = match compiled {
|
||||
Ok(dxc_result) => match dxc_result.get_result() {
|
||||
Ok(dxc_blob) => {
|
||||
// Validate the shader.
|
||||
match dxc_container.validator.validate(dxc_blob) {
|
||||
Ok(validated_blob) => (
|
||||
Ok(crate::dx12::CompiledShader::Dxc(validated_blob.to_vec())),
|
||||
log::Level::Info,
|
||||
),
|
||||
Err(e) => (
|
||||
Err(crate::PipelineError::Linkage(
|
||||
stage_bit,
|
||||
format!(
|
||||
"DXC validation error: {:?}\n{:?}",
|
||||
get_error_string_from_dxc_result(&dxc_container.library, &e.0)
|
||||
.unwrap_or_default(),
|
||||
e.1
|
||||
),
|
||||
)),
|
||||
log::Level::Error,
|
||||
),
|
||||
let compile_res: Dxc::IDxcResult = unsafe {
|
||||
dxc_container
|
||||
.compiler
|
||||
.Compile(&buffer, Some(&compile_args), None)
|
||||
}
|
||||
.into_device_result("Compile")?;
|
||||
|
||||
drop(compile_args);
|
||||
drop(source_name);
|
||||
drop(raw_ep);
|
||||
drop(full_stage);
|
||||
|
||||
let err_blob = get_output::<Dxc::IDxcBlobUtf8>(&compile_res, Dxc::DXC_OUT_ERRORS)?;
|
||||
|
||||
let len = unsafe { err_blob.GetStringLength() };
|
||||
if len != 0 {
|
||||
let err = as_err_str(&err_blob)?;
|
||||
return Err(crate::PipelineError::Linkage(
|
||||
stage_bit,
|
||||
format!("DXC compile error: {err}"),
|
||||
));
|
||||
}
|
||||
Err(e) => (
|
||||
Err(crate::PipelineError::Linkage(
|
||||
stage_bit,
|
||||
format!("DXC compile error: {e}"),
|
||||
)),
|
||||
log::Level::Error,
|
||||
),
|
||||
},
|
||||
Err(e) => (
|
||||
Err(crate::PipelineError::Linkage(
|
||||
stage_bit,
|
||||
format!(
|
||||
"DXC compile error: {}",
|
||||
get_error_string_from_dxc_result(&dxc_container.library, &e.0)
|
||||
.unwrap_or_default()
|
||||
),
|
||||
)),
|
||||
log::Level::Error,
|
||||
),
|
||||
|
||||
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)
|
||||
}
|
||||
.into_device_result("Validate")?;
|
||||
|
||||
unsafe { res.GetErrorBuffer() }.into_device_result("GetErrorBuffer")?
|
||||
};
|
||||
|
||||
(result, log_level)
|
||||
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}"),
|
||||
));
|
||||
}
|
||||
|
||||
impl From<hassle_rs::HassleError> for crate::DeviceError {
|
||||
fn from(value: hassle_rs::HassleError) -> Self {
|
||||
match value {
|
||||
hassle_rs::HassleError::Win32Error(e) => {
|
||||
// TODO: This returns an HRESULT, should we try and use the associated Windows error message?
|
||||
log::error!("Win32 error: {e:?}");
|
||||
crate::DeviceError::Lost
|
||||
}
|
||||
hassle_rs::HassleError::LoadLibraryError { filename, inner } => {
|
||||
log::error!("Failed to load dxc library {filename:?}. Inner error: {inner:?}");
|
||||
crate::DeviceError::Lost
|
||||
}
|
||||
hassle_rs::HassleError::LibLoadingError(e) => {
|
||||
log::error!("Failed to load dxc library. {e:?}");
|
||||
crate::DeviceError::Lost
|
||||
}
|
||||
hassle_rs::HassleError::WindowsOnly(e) => {
|
||||
log::error!("Signing with dxil.dll is only supported on Windows. {e:?}");
|
||||
crate::DeviceError::Lost
|
||||
}
|
||||
// `ValidationError` and `CompileError` should never happen in a context involving `DeviceError`
|
||||
hassle_rs::HassleError::ValidationError(_e) => unimplemented!(),
|
||||
hassle_rs::HassleError::CompileError(_e) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_error_string_from_dxc_result(
|
||||
library: &hassle_rs::DxcLibrary,
|
||||
error: &hassle_rs::DxcOperationResult,
|
||||
) -> Result<String, hassle_rs::HassleError> {
|
||||
error
|
||||
.get_error_buffer()
|
||||
.and_then(|error| library.get_blob_as_string(&hassle_rs::DxcBlob::from(error)))
|
||||
}
|
||||
}
|
||||
|
||||
// These are stubs for when the `dxc_shader_compiler` feature is disabled.
|
||||
#[cfg(not(feature = "dxc_shader_compiler"))]
|
||||
mod dxc {
|
||||
use std::ffi::CStr;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) struct DxcContainer {}
|
||||
|
||||
pub(crate) fn get_dxc_container(
|
||||
_dxc_path: Option<PathBuf>,
|
||||
_dxil_path: Option<PathBuf>,
|
||||
) -> Result<Option<DxcContainer>, crate::DeviceError> {
|
||||
// Falls back to Fxc and logs an error.
|
||||
log::error!("DXC shader compiler was requested on Instance creation, but the DXC feature is disabled. Enable the `dxc_shader_compiler` feature on wgpu_hal to use DXC.");
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// It shouldn't be possible that this gets called with the `dxc_shader_compiler` feature disabled.
|
||||
pub(crate) fn compile_dxc(
|
||||
_device: &crate::dx12::Device,
|
||||
_source: &str,
|
||||
_source_name: Option<&CStr>,
|
||||
_raw_ep: &str,
|
||||
_stage_bit: wgt::ShaderStages,
|
||||
_full_stage: String,
|
||||
_dxc_container: &DxcContainer,
|
||||
) -> (
|
||||
Result<crate::dx12::CompiledShader, crate::PipelineError>,
|
||||
log::Level,
|
||||
) {
|
||||
unimplemented!("Something went really wrong, please report this. Attempted to compile shader with DXC, but the DXC feature is disabled. Enable the `dxc_shader_compiler` feature on wgpu_hal to use DXC.");
|
||||
}
|
||||
Ok(crate::dx12::CompiledShader::Dxc(blob))
|
||||
}
|
||||
|
@ -7334,10 +7334,6 @@ impl Default for ShaderBoundChecks {
|
||||
|
||||
/// Selects which DX12 shader compiler to use.
|
||||
///
|
||||
/// If the `wgpu-hal/dx12-shader-compiler` feature isn't enabled then this will fall back
|
||||
/// to the Fxc compiler at runtime and log an error.
|
||||
/// This feature is always enabled when using `wgpu`.
|
||||
///
|
||||
/// If the `Dxc` option is selected, but `dxcompiler.dll` and `dxil.dll` files aren't found,
|
||||
/// then this will fall back to the Fxc compiler at runtime and log an error.
|
||||
///
|
||||
@ -7354,6 +7350,10 @@ pub enum Dx12Compiler {
|
||||
///
|
||||
/// However, it requires both `dxcompiler.dll` and `dxil.dll` to be shipped with the application.
|
||||
/// These files can be downloaded from <https://github.com/microsoft/DirectXShaderCompiler/releases>.
|
||||
///
|
||||
/// Minimum supported version: [v1.5.2010](https://github.com/microsoft/DirectXShaderCompiler/releases/tag/v1.5.2010)
|
||||
///
|
||||
/// It also requires WDDM 2.1 (Windows 10 version 1607).
|
||||
Dxc {
|
||||
/// Path to the `dxil.dll` file, or path to the directory containing `dxil.dll` file. Passing `None` will use standard platform specific dll loading rules.
|
||||
dxil_path: Option<PathBuf>,
|
||||
|
@ -161,10 +161,7 @@ hal = { workspace = true }
|
||||
hal = { workspace = true, features = ["renderdoc"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
hal = { workspace = true, features = [
|
||||
"dxc_shader_compiler",
|
||||
"renderdoc",
|
||||
] }
|
||||
hal = { workspace = true, features = ["renderdoc"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.hal]
|
||||
workspace = true
|
||||
|
Loading…
Reference in New Issue
Block a user