Skip to content

Commit f1a1955

Browse files
committed
dxgi: move the d3d12.dll loader to auxil::dxgi, share across DX12/Vulkan
The DX12 backend's d3d12.dll loader (D3D12Lib) and its error types move to auxil::dxgi::library so the Vulkan DXGI interop path can create its interop device through the same runtime-loaded entry points. Each entry point resolves against the windows-crate PFN_* alias. DX12 keeps using the loader via a re-export, so its call sites and public API are unchanged. Enable Win32_Graphics_Direct3D12 for the vulkan feature on Windows (replacing Win32_Graphics_Direct3D11) so the shared loader compiles in vulkan-only builds.
1 parent 899538d commit f1a1955

4 files changed

Lines changed: 193 additions & 181 deletions

File tree

wgpu-hal/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ vulkan = [
109109
# Windows-only: shared DXGI presentation path (`auxil::dxgi`) for the opt-in
110110
# DXGI flip-model swapchain. Inert on non-Windows targets.
111111
"windows/Win32_Graphics_Direct3D",
112-
"windows/Win32_Graphics_Direct3D11",
112+
"windows/Win32_Graphics_Direct3D12",
113113
"windows/Win32_Graphics_DirectComposition",
114114
"windows/Win32_Graphics_Dxgi",
115115
"windows/Win32_Graphics_Dxgi_Common",

wgpu-hal/src/auxil/dxgi/library.rs

Lines changed: 184 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use core::ffi;
1+
use core::{error::Error, ffi, fmt};
22

3-
use windows::{core::Interface as _, Win32::Graphics::Dxgi};
3+
use windows::{
4+
core::{IUnknown, Interface as _, Ref},
5+
Win32::Graphics::{Direct3D, Direct3D12, Dxgi},
6+
};
47

5-
use crate::auxil::dxgi::result::HResult as _;
8+
use crate::auxil::dxgi::{factory::DxgiAdapter, result::HResult as _};
69

710
#[derive(Debug)]
811
pub(crate) struct DynLib {
@@ -122,3 +125,181 @@ impl DxgiLib {
122125
result__.ok_or(crate::DeviceError::Unexpected)
123126
}
124127
}
128+
129+
/// Loads `d3d12.dll` at runtime, the same way [`DxgiLib`] loads `dxgi.dll`, so the DLL is never a
130+
/// load-time import. This keeps the binary loadable on systems without D3D12 (e.g. Windows 7/8),
131+
/// where a DXGI/D3D12 swapchain simply isn't available. Shared by the DX12 backend and the Vulkan
132+
/// DXGI interop swapchain.
133+
#[derive(Debug)]
134+
pub(crate) struct D3D12Lib {
135+
lib: DynLib,
136+
}
137+
138+
impl D3D12Lib {
139+
pub(crate) fn new() -> Result<Self, libloading::Error> {
140+
unsafe { DynLib::new("d3d12.dll").map(|lib| Self { lib }) }
141+
}
142+
143+
pub(crate) fn create_device(
144+
&self,
145+
adapter: &DxgiAdapter,
146+
feature_level: Direct3D::D3D_FEATURE_LEVEL,
147+
) -> Result<Direct3D12::ID3D12Device, CreateDeviceError> {
148+
// Calls windows::Win32::Graphics::Direct3D12::D3D12CreateDevice on d3d12.dll.
149+
let func: libloading::Symbol<Direct3D12::PFN_D3D12_CREATE_DEVICE> =
150+
unsafe { self.lib.get(c"D3D12CreateDevice".to_bytes()) }
151+
.map_err(|_| CreateDeviceError::GetProcAddress)?;
152+
let func = (*func).ok_or(CreateDeviceError::GetProcAddress)?;
153+
154+
// `Ref<IUnknown>` is a `repr(transparent)` non-owning borrow of the COM pointer (it does not
155+
// touch the refcount); the adapter outlives this synchronous call.
156+
let adapter: Ref<IUnknown> = unsafe { core::mem::transmute(adapter.as_raw()) };
157+
158+
let mut result__: Option<Direct3D12::ID3D12Device> = None;
159+
let res = unsafe {
160+
func(
161+
adapter,
162+
feature_level,
163+
&Direct3D12::ID3D12Device::IID,
164+
<*mut _>::cast(&mut result__),
165+
)
166+
};
167+
168+
if res.is_err() {
169+
return Err(CreateDeviceError::D3D12CreateDevice(res));
170+
}
171+
result__.ok_or(CreateDeviceError::RetDeviceIsNull)
172+
}
173+
174+
#[cfg(dx12)]
175+
pub(crate) fn serialize_root_signature(
176+
&self,
177+
version: Direct3D12::D3D_ROOT_SIGNATURE_VERSION,
178+
parameters: &[Direct3D12::D3D12_ROOT_PARAMETER],
179+
static_samplers: &[Direct3D12::D3D12_STATIC_SAMPLER_DESC],
180+
flags: Direct3D12::D3D12_ROOT_SIGNATURE_FLAGS,
181+
) -> Result<Direct3D::ID3DBlob, crate::DeviceError> {
182+
// Calls windows::Win32::Graphics::Direct3D12::D3D12SerializeRootSignature on d3d12.dll.
183+
let func: libloading::Symbol<Direct3D12::PFN_D3D12_SERIALIZE_ROOT_SIGNATURE> =
184+
unsafe { self.lib.get(c"D3D12SerializeRootSignature".to_bytes()) }?;
185+
let func = (*func).ok_or(crate::DeviceError::Unexpected)?;
186+
187+
let desc = Direct3D12::D3D12_ROOT_SIGNATURE_DESC {
188+
NumParameters: parameters.len() as _,
189+
pParameters: parameters.as_ptr(),
190+
NumStaticSamplers: static_samplers.len() as _,
191+
pStaticSamplers: static_samplers.as_ptr(),
192+
Flags: flags,
193+
};
194+
195+
let mut blob: Option<Direct3D::ID3DBlob> = None;
196+
let mut error: Option<Direct3D::ID3DBlob> = None;
197+
unsafe { func(&desc, version, (&mut blob).into(), (&mut error).into()) }
198+
.ok()
199+
.into_device_result("Root signature serialization")?;
200+
201+
if let Some(error) = error {
202+
let message = unsafe {
203+
let slice = core::slice::from_raw_parts(
204+
error.GetBufferPointer().cast::<u8>(),
205+
error.GetBufferSize(),
206+
);
207+
ffi::CStr::from_bytes_until_nul(slice)
208+
};
209+
log::error!(
210+
"Root signature serialization error: {:?}",
211+
message.unwrap().to_str().unwrap()
212+
);
213+
return Err(crate::DeviceError::Unexpected); // could be hal_usage_error or hal_internal_error
214+
}
215+
216+
blob.ok_or(crate::DeviceError::Unexpected)
217+
}
218+
219+
pub(crate) fn debug_interface(
220+
&self,
221+
) -> Result<Option<Direct3D12::ID3D12Debug>, crate::DeviceError> {
222+
// Calls windows::Win32::Graphics::Direct3D12::D3D12GetDebugInterface on d3d12.dll.
223+
let func: libloading::Symbol<Direct3D12::PFN_D3D12_GET_DEBUG_INTERFACE> =
224+
unsafe { self.lib.get(c"D3D12GetDebugInterface".to_bytes()) }?;
225+
let func = (*func).ok_or(crate::DeviceError::Unexpected)?;
226+
227+
let mut result__ = None;
228+
let res =
229+
unsafe { func(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)) }.ok();
230+
231+
if let Err(ref err) = res {
232+
if err.code() == Dxgi::DXGI_ERROR_SDK_COMPONENT_MISSING {
233+
return Ok(None);
234+
}
235+
}
236+
237+
res.into_device_result("GetDebugInterface")?;
238+
239+
result__.ok_or(crate::DeviceError::Unexpected).map(Some)
240+
}
241+
242+
/// Calls D3D12GetInterface to obtain a COM interface by CLSID and IID.
243+
///
244+
/// This is used by the Independent Devices API to obtain `ID3D12SDKConfiguration1`.
245+
#[cfg(dx12)]
246+
pub(crate) fn get_interface<T: windows_core::Interface>(
247+
&self,
248+
clsid: &windows_core::GUID,
249+
) -> Result<T, GetInterfaceError> {
250+
// Calls windows::Win32::Graphics::Direct3D12::D3D12GetInterface on d3d12.dll.
251+
let func: libloading::Symbol<Direct3D12::PFN_D3D12_GET_INTERFACE> =
252+
unsafe { self.lib.get(c"D3D12GetInterface".to_bytes()) }
253+
.map_err(|_| GetInterfaceError::GetProcAddress)?;
254+
let func = (*func).ok_or(GetInterfaceError::GetProcAddress)?;
255+
256+
let mut result__: Option<T> = None;
257+
let res = unsafe { func(clsid, &T::IID, <*mut _>::cast(&mut result__)) };
258+
259+
if res.is_err() {
260+
return Err(GetInterfaceError::D3D12GetInterface(res));
261+
}
262+
result__.ok_or(GetInterfaceError::RetIsNull)
263+
}
264+
}
265+
266+
#[derive(Clone, Copy, Debug)]
267+
pub enum CreateDeviceError {
268+
GetProcAddress,
269+
D3D12CreateDevice(windows_core::HRESULT),
270+
RetDeviceIsNull,
271+
}
272+
273+
impl fmt::Display for CreateDeviceError {
274+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275+
match self {
276+
Self::GetProcAddress => write!(f, "D3D12CreateDevice not found in d3d12.dll"),
277+
Self::D3D12CreateDevice(hr) => write!(f, "D3D12CreateDevice failed: {hr}"),
278+
Self::RetDeviceIsNull => write!(f, "D3D12CreateDevice returned null"),
279+
}
280+
}
281+
}
282+
283+
impl Error for CreateDeviceError {}
284+
285+
#[cfg(dx12)]
286+
#[derive(Clone, Copy, Debug)]
287+
pub(crate) enum GetInterfaceError {
288+
GetProcAddress,
289+
D3D12GetInterface(windows_core::HRESULT),
290+
RetIsNull,
291+
}
292+
293+
#[cfg(dx12)]
294+
impl fmt::Display for GetInterfaceError {
295+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296+
match self {
297+
Self::GetProcAddress => write!(f, "D3D12GetInterface not found in d3d12.dll"),
298+
Self::D3D12GetInterface(hr) => write!(f, "D3D12GetInterface failed: {hr}"),
299+
Self::RetIsNull => write!(f, "D3D12GetInterface returned null"),
300+
}
301+
}
302+
}
303+
304+
#[cfg(dx12)]
305+
impl Error for GetInterfaceError {}

wgpu-hal/src/dx12/device.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use windows::{
2020
},
2121
};
2222

23-
use super::{conv, descriptor, D3D12Lib};
23+
use super::{conv, descriptor, D3D12Lib, D3DBlob};
2424
use crate::{
2525
auxil::{
2626
self,
@@ -1378,12 +1378,12 @@ impl crate::Device for super::Device {
13781378
(None, None)
13791379
};
13801380

1381-
let blob = self.library.serialize_root_signature(
1381+
let blob = D3DBlob(self.library.serialize_root_signature(
13821382
Direct3D12::D3D_ROOT_SIGNATURE_VERSION_1_0,
13831383
&parameters,
13841384
&[],
13851385
Direct3D12::D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT,
1386-
)?;
1386+
)?);
13871387

13881388
let raw = unsafe {
13891389
self.raw

0 commit comments

Comments
 (0)