Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions wgpu-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ vulkan = [
"dep:smallvec",
"dep:windows",
"windows/Win32",
"windows/Win32_System",
"windows/Win32_System_Diagnostics",
"windows/Win32_System_Diagnostics_Debug",
"windows/Win32_System_Kernel",
"windows/Win32_Graphics_Dxgi_Common",
"windows/Win32_Graphics_Direct3D",
"windows/Win32_Graphics_Direct3D12",
"windows/Win32_System_Performance",
]
gles = [
"naga/glsl-out",
Expand Down
28 changes: 26 additions & 2 deletions wgpu-hal/src/auxil/dxgi/factory.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use alloc::{string::String, vec::Vec};
use core::ops::Deref;

use windows::{core::Interface as _, Win32::Graphics::Dxgi};
use windows::{
core::Interface as _,
Win32::{Foundation, Graphics::Dxgi},
};

use crate::dx12::DxgiLib;
use crate::auxil::dxgi::library::DxgiLib;

use super::result::HResult as _;

Expand Down Expand Up @@ -139,6 +142,27 @@ impl DxgiFactory {
Self::Factory6(f) => Some(f),
}
}

/// Returns `true` if the factory supports the ALLOW_TEARING present feature.
pub fn supports_allow_tearing(&self) -> bool {
let mut supports_allow_tearing = false;
if let Some(factory5) = self.as_factory5() {
let mut allow_tearing = Foundation::FALSE;
let hr = unsafe {
factory5.CheckFeatureSupport(
Dxgi::DXGI_FEATURE_PRESENT_ALLOW_TEARING,
<*mut _>::cast(&mut allow_tearing),
size_of_val(&allow_tearing) as u32,
)
};

match hr {
Err(err) => log::warn!("Unable to check for tearing support: {err}"),
Ok(()) => supports_allow_tearing = true,
}
}
supports_allow_tearing
}
}

pub fn create_factory(
Expand Down
263 changes: 263 additions & 0 deletions wgpu-hal/src/auxil/dxgi/library.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
use core::ops::Deref;
use std::ffi;
use windows::core::Interface as _;
use windows::Win32::Graphics::{Direct3D, Direct3D12, Dxgi};

use crate::auxil::dxgi::{factory::DxgiAdapter, result::HResult as _};

#[derive(Debug)]
pub(crate) struct DynLib {
inner: libloading::Library,
}

impl DynLib {
pub unsafe fn new<P>(filename: P) -> Result<Self, libloading::Error>
where
P: AsRef<ffi::OsStr>,
{
unsafe { libloading::Library::new(filename) }.map(|inner| Self { inner })
}

pub unsafe fn get<T>(
&self,
symbol: &[u8],
) -> Result<libloading::Symbol<'_, T>, crate::DeviceError> {
unsafe { self.inner.get(symbol) }.map_err(|e| match e {
libloading::Error::GetProcAddress { .. } | libloading::Error::GetProcAddressUnknown => {
crate::DeviceError::Unexpected
}
libloading::Error::IncompatibleSize
| libloading::Error::CreateCString { .. }
| libloading::Error::CreateCStringWithTrailing { .. } => crate::hal_internal_error(e),
_ => crate::DeviceError::Unexpected, // could be unreachable!() but we prefer to be more robust
})
}
}

#[derive(Debug)]
pub(crate) struct D3D12Lib {
lib: DynLib,
}

impl D3D12Lib {
pub fn new() -> Result<Self, libloading::Error> {
unsafe { DynLib::new("d3d12.dll").map(|lib| Self { lib }) }
}

pub fn create_device(
&self,
adapter: &DxgiAdapter,
feature_level: Direct3D::D3D_FEATURE_LEVEL,
) -> Result<Option<Direct3D12::ID3D12Device>, crate::DeviceError> {
// Calls windows::Win32::Graphics::Direct3D12::D3D12CreateDevice on d3d12.dll
type Fun = extern "system" fn(
padapter: *mut ffi::c_void,
minimumfeaturelevel: Direct3D::D3D_FEATURE_LEVEL,
riid: *const windows::core::GUID,
ppdevice: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"D3D12CreateDevice".to_bytes()) }?;

let mut result__: Option<Direct3D12::ID3D12Device> = None;

let res = (func)(
adapter.as_raw(),
feature_level,
// TODO: Generic?
&Direct3D12::ID3D12Device::IID,
<*mut _>::cast(&mut result__),
)
.ok();

if let Err(ref err) = res {
match err.code() {
Dxgi::DXGI_ERROR_UNSUPPORTED => return Ok(None),
Dxgi::DXGI_ERROR_DRIVER_INTERNAL_ERROR => return Err(crate::DeviceError::Lost),
_ => {}
}
}

res.into_device_result("Device creation")?;

result__.ok_or(crate::DeviceError::Unexpected).map(Some)
}

pub fn serialize_root_signature(
&self,
version: Direct3D12::D3D_ROOT_SIGNATURE_VERSION,
parameters: &[Direct3D12::D3D12_ROOT_PARAMETER],
static_samplers: &[Direct3D12::D3D12_STATIC_SAMPLER_DESC],
flags: Direct3D12::D3D12_ROOT_SIGNATURE_FLAGS,
) -> Result<D3DBlob, crate::DeviceError> {
// Calls windows::Win32::Graphics::Direct3D12::D3D12SerializeRootSignature on d3d12.dll
type Fun = extern "system" fn(
prootsignature: *const Direct3D12::D3D12_ROOT_SIGNATURE_DESC,
version: Direct3D12::D3D_ROOT_SIGNATURE_VERSION,
ppblob: *mut *mut ffi::c_void,
pperrorblob: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"D3D12SerializeRootSignature".to_bytes()) }?;

let desc = Direct3D12::D3D12_ROOT_SIGNATURE_DESC {
NumParameters: parameters.len() as _,
pParameters: parameters.as_ptr(),
NumStaticSamplers: static_samplers.len() as _,
pStaticSamplers: static_samplers.as_ptr(),
Flags: flags,
};

let mut blob = None;
let mut error = None::<Direct3D::ID3DBlob>;
(func)(
&desc,
version,
<*mut _>::cast(&mut blob),
<*mut _>::cast(&mut error),
)
.ok()
.into_device_result("Root signature serialization")?;

if let Some(error) = error {
let error = D3DBlob(error);
log::error!(
"Root signature serialization error: {:?}",
unsafe { error.as_c_str() }.unwrap().to_str().unwrap()
);
return Err(crate::DeviceError::Unexpected); // could be hal_usage_error or hal_internal_error
}

blob.ok_or(crate::DeviceError::Unexpected)
}

pub fn debug_interface(&self) -> Result<Option<Direct3D12::ID3D12Debug>, crate::DeviceError> {
// Calls windows::Win32::Graphics::Direct3D12::D3D12GetDebugInterface on d3d12.dll
type Fun = extern "system" fn(
riid: *const windows::core::GUID,
ppvdebug: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"D3D12GetDebugInterface".to_bytes()) }?;

let mut result__ = None;

let res = (func)(&Direct3D12::ID3D12Debug::IID, <*mut _>::cast(&mut result__)).ok();

if let Err(ref err) = res {
match err.code() {
Dxgi::DXGI_ERROR_SDK_COMPONENT_MISSING => return Ok(None),
_ => {}
}
}

res.into_device_result("GetDebugInterface")?;

result__.ok_or(crate::DeviceError::Unexpected).map(Some)
}
}

#[derive(Debug)]
pub(crate) struct DxgiLib {
lib: DynLib,
}

impl DxgiLib {
pub fn new() -> Result<Self, libloading::Error> {
unsafe { DynLib::new("dxgi.dll").map(|lib| Self { lib }) }
}

/// Will error with crate::DeviceError::Unexpected if DXGI 1.3 is not available.
pub fn debug_interface1(&self) -> Result<Option<Dxgi::IDXGIInfoQueue>, crate::DeviceError> {
// Calls windows::Win32::Graphics::Dxgi::DXGIGetDebugInterface1 on dxgi.dll
type Fun = extern "system" fn(
flags: u32,
riid: *const windows::core::GUID,
pdebug: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"DXGIGetDebugInterface1".to_bytes()) }?;

let mut result__ = None;

let res = (func)(0, &Dxgi::IDXGIInfoQueue::IID, <*mut _>::cast(&mut result__)).ok();

if let Err(ref err) = res {
match err.code() {
Dxgi::DXGI_ERROR_SDK_COMPONENT_MISSING => return Ok(None),
_ => {}
}
}

res.into_device_result("debug_interface1")?;

result__.ok_or(crate::DeviceError::Unexpected).map(Some)
}

/// Will error with crate::DeviceError::Unexpected if DXGI 1.4 is not available.
pub fn create_factory4(
&self,
factory_flags: Dxgi::DXGI_CREATE_FACTORY_FLAGS,
) -> Result<Dxgi::IDXGIFactory4, crate::DeviceError> {
// Calls windows::Win32::Graphics::Dxgi::CreateDXGIFactory2 on dxgi.dll
type Fun = extern "system" fn(
flags: Dxgi::DXGI_CREATE_FACTORY_FLAGS,
riid: *const windows::core::GUID,
ppfactory: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"CreateDXGIFactory2".to_bytes()) }?;

let mut result__ = None;

(func)(
factory_flags,
&Dxgi::IDXGIFactory4::IID,
<*mut _>::cast(&mut result__),
)
.ok()
.into_device_result("create_factory4")?;

result__.ok_or(crate::DeviceError::Unexpected)
}

/// Will error with crate::DeviceError::Unexpected if DXGI 1.3 is not available.
pub fn create_factory_media(&self) -> Result<Dxgi::IDXGIFactoryMedia, crate::DeviceError> {
// Calls windows::Win32::Graphics::Dxgi::CreateDXGIFactory1 on dxgi.dll
type Fun = extern "system" fn(
riid: *const windows::core::GUID,
ppfactory: *mut *mut ffi::c_void,
) -> windows::core::HRESULT;
let func: libloading::Symbol<Fun> =
unsafe { self.lib.get(c"CreateDXGIFactory1".to_bytes()) }?;

let mut result__ = None;

// https://learn.microsoft.com/en-us/windows/win32/api/dxgi1_3/nn-dxgi1_3-idxgifactorymedia
(func)(&Dxgi::IDXGIFactoryMedia::IID, <*mut _>::cast(&mut result__))
.ok()
.into_device_result("create_factory_media")?;

result__.ok_or(crate::DeviceError::Unexpected)
}
}

pub(crate) struct D3DBlob(Direct3D::ID3DBlob);

impl Deref for D3DBlob {
type Target = Direct3D::ID3DBlob;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl D3DBlob {
pub unsafe fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.GetBufferPointer().cast(), self.GetBufferSize()) }
}

pub unsafe fn as_c_str(&self) -> Result<&ffi::CStr, ffi::FromBytesUntilNulError> {
ffi::CStr::from_bytes_until_nul(unsafe { self.as_slice() })
}
}
4 changes: 4 additions & 0 deletions wgpu-hal/src/auxil/dxgi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// If we're exclusively on vulkan, ignore unused code warnings.
#![cfg_attr(all(windows, vulkan, not(dx12)), allow(dead_code))]

pub mod conv;
pub mod exception;
pub mod factory;
pub mod library;
pub mod name;
pub mod result;
pub mod time;
2 changes: 1 addition & 1 deletion wgpu-hal/src/auxil/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(dx12)]
#[cfg(any(dx12, all(windows, vulkan)))]
pub(super) mod dxgi;

#[cfg(all(native, feature = "renderdoc"))]
Expand Down
3 changes: 1 addition & 2 deletions wgpu-hal/src/dx12/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ use windows::{
},
};

use super::D3D12Lib;
use crate::{
auxil::{
self,
dxgi::{factory::DxgiAdapter, result::HResult},
dxgi::{factory::DxgiAdapter, library::D3D12Lib, result::HResult},
},
dx12::{dcomp::DCompLib, shader_compilation, SurfaceTarget},
};
Expand Down
2 changes: 1 addition & 1 deletion wgpu-hal/src/dx12/dcomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use once_cell::sync::Lazy;
use windows::Win32::{Foundation::HWND, Graphics::DirectComposition};
use windows_core::Interface;

use super::DynLib;
use crate::auxil::dxgi::library::DynLib;

// Lazy-loaded DirectComposition library
#[derive(Debug)]
Expand Down
4 changes: 2 additions & 2 deletions wgpu-hal/src/dx12/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ use windows::{
},
};

use super::{conv, descriptor, D3D12Lib};
use super::{conv, descriptor};
use crate::{
auxil::{
self,
dxgi::{name::ObjectExt, result::HResult},
dxgi::{library::D3D12Lib, name::ObjectExt, result::HResult},
},
dx12::{
borrow_optional_interface_temporarily, shader_compilation, suballocation, DCompLib,
Expand Down
Loading
Loading