Skip to content

Commit 302be45

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 a543b43 commit 302be45

7 files changed

Lines changed: 248 additions & 196 deletions

File tree

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ use core::ops::Deref;
44
use windows::core::Interface as _;
55
use windows::Win32::Graphics::{Direct3D, Direct3D12};
66

7-
use super::D3D12Lib;
8-
use crate::auxil::dxgi::factory::DxgiAdapter;
7+
use super::factory::DxgiAdapter;
8+
use super::library::{CreateDeviceError, D3D12Lib, GetInterfaceError};
99

1010
/// Abstraction over D3D12 device creation.
1111
///
1212
/// Supports two paths:
1313
/// - **Independent**: Uses `ID3D12DeviceFactory` from the Agility SDK's Independent Devices API.
1414
/// - **Legacy**: Uses the traditional `D3D12CreateDevice` export.
15-
pub(super) enum DeviceFactory {
15+
pub(crate) enum DeviceFactory {
1616
/// Uses `ID3D12DeviceFactory` from the Independent Devices API.
1717
Independent(Direct3D12::ID3D12DeviceFactory),
1818
/// Uses the traditional `D3D12CreateDevice` export.
@@ -28,7 +28,7 @@ impl DeviceFactory {
2828
/// - [`Fallback`](wgt::Dx12AgilitySDKLoadFailure::Fallback): logs a warning and
2929
/// returns `Ok(Legacy)`.
3030
/// - [`Error`](wgt::Dx12AgilitySDKLoadFailure::Error): returns an `Err`.
31-
pub(super) fn new(
31+
pub(crate) fn new(
3232
lib: &D3D12Lib,
3333
agility_sdk: Option<&wgt::Dx12AgilitySDK>,
3434
) -> Result<Self, crate::InstanceError> {
@@ -93,7 +93,7 @@ impl DeviceFactory {
9393
/// - **Legacy**: configures debug globally via `D3D12GetDebugInterface`.
9494
/// - **Independent**: uses `GetConfigurationInterface` to get an
9595
/// `ID3D12Debug` scoped to the factory.
96-
pub(super) fn enable_debug_layer(&self, lib: &D3D12Lib, flags: wgt::InstanceFlags) {
96+
pub(crate) fn enable_debug_layer(&self, lib: &D3D12Lib, flags: wgt::InstanceFlags) {
9797
if !flags
9898
.intersects(wgt::InstanceFlags::VALIDATION | wgt::InstanceFlags::GPU_BASED_VALIDATION)
9999
{
@@ -137,19 +137,19 @@ impl DeviceFactory {
137137
}
138138

139139
/// Create a D3D12 device using the appropriate method.
140-
pub(super) fn create_device(
140+
pub(crate) fn create_device(
141141
&self,
142142
lib: &Arc<D3D12Lib>,
143143
adapter: &DxgiAdapter,
144144
feature_level: Direct3D::D3D_FEATURE_LEVEL,
145-
) -> Result<Direct3D12::ID3D12Device, super::CreateDeviceError> {
145+
) -> Result<Direct3D12::ID3D12Device, CreateDeviceError> {
146146
match self {
147147
Self::Independent(factory) => {
148148
let mut result__: Option<Direct3D12::ID3D12Device> = None;
149149
unsafe { factory.CreateDevice(adapter.deref(), feature_level, &mut result__) }
150-
.map_err(|e| super::CreateDeviceError::D3D12CreateDevice(e.into()))?;
150+
.map_err(|e| CreateDeviceError::D3D12CreateDevice(e.into()))?;
151151

152-
result__.ok_or(super::CreateDeviceError::RetDeviceIsNull)
152+
result__.ok_or(CreateDeviceError::RetDeviceIsNull)
153153
}
154154
Self::Legacy => lib.create_device(adapter, feature_level),
155155
}
@@ -168,7 +168,7 @@ impl core::fmt::Debug for DeviceFactory {
168168
#[derive(Debug, thiserror::Error)]
169169
enum DeviceFactoryError {
170170
#[error("failed to get ID3D12SDKConfiguration1: {0}")]
171-
GetInterface(super::GetInterfaceError),
171+
GetInterface(GetInterfaceError),
172172
#[error("SDK path contains null bytes")]
173173
InvalidPath,
174174
#[error("CreateDeviceFactory failed: {0}")]

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

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

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

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

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// DXGI presentation path, so cfgs are used to include the appropriate bits for each backend.
33
pub mod conv;
44
pub mod dcomp;
5+
pub mod device_factory;
56
#[cfg(dx12)]
67
pub mod exception;
78
pub mod factory;

wgpu-hal/src/dx12/adapter.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ use super::D3D12Lib;
2020
use crate::{
2121
auxil::{
2222
self,
23-
dxgi::{dcomp::DCompLib, factory::DxgiAdapter, result::HResult},
23+
dxgi::{
24+
dcomp::DCompLib, device_factory::DeviceFactory, factory::DxgiAdapter, result::HResult,
25+
},
2426
},
25-
dx12::{device_creation::DeviceFactory, shader_compilation, FeatureLevel, ShaderModel},
27+
dx12::{shader_compilation, FeatureLevel, ShaderModel},
2628
};
2729

2830
impl Drop for super::Adapter {

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

wgpu-hal/src/dx12/instance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use windows::Win32::Foundation;
66
use crate::{
77
auxil::{
88
self,
9-
dxgi::{dcomp::DCompLib, swapchain::SurfaceTarget},
9+
dxgi::{dcomp::DCompLib, device_factory::DeviceFactory, swapchain::SurfaceTarget},
1010
},
11-
dx12::{device_creation::DeviceFactory, shader_compilation::CompilerContainer, D3D12Lib},
11+
dx12::{shader_compilation::CompilerContainer, D3D12Lib},
1212
};
1313

1414
impl crate::Instance for super::Instance {

0 commit comments

Comments
 (0)