use windows::Graphics::DirectX::Direct3D11::IDirect3DDevice;
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Graphics::Direct3D::{
D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_9_1, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
};
use windows::Win32::Graphics::Direct3D11::{
D3D11_CPU_ACCESS_READ, D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION,
D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
use windows::Win32::Graphics::Dxgi::IDXGIDevice;
use windows::Win32::System::WinRT::Direct3D11::CreateDirect3D11DeviceFromDXGIDevice;
use windows::core::Interface;
#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
pub enum Error {
#[error("Failed to create DirectX device with the recommended feature levels")]
FeatureLevelNotSatisfied,
#[error("Windows API Error: {0}")]
WindowsError(#[from] windows::core::Error),
}
pub struct SendDirectX<T>(pub T);
impl<T> SendDirectX<T> {
#[inline]
#[must_use]
pub const fn new(device: T) -> Self {
Self(device)
}
}
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T> Send for SendDirectX<T> {}
#[inline]
pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Error> {
let feature_flags = [
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1,
];
let mut d3d_device = None;
let mut feature_level = D3D_FEATURE_LEVEL::default();
let mut d3d_device_context = None;
unsafe {
D3D11CreateDevice(
None,
D3D_DRIVER_TYPE_HARDWARE,
HMODULE::default(),
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
Some(&feature_flags),
D3D11_SDK_VERSION,
Some(&mut d3d_device),
Some(&mut feature_level),
Some(&mut d3d_device_context),
)?;
};
if feature_level.0 < D3D_FEATURE_LEVEL_11_0.0 {
return Err(Error::FeatureLevelNotSatisfied);
}
Ok((d3d_device.unwrap(), d3d_device_context.unwrap()))
}
#[inline]
pub fn create_direct3d_device(d3d_device: &ID3D11Device) -> Result<IDirect3DDevice, Error> {
let dxgi_device: IDXGIDevice = d3d_device.cast()?;
let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device)? };
let device: IDirect3DDevice = inspectable.cast()?;
Ok(device)
}
pub struct StagingTexture {
inner: ID3D11Texture2D,
desc: D3D11_TEXTURE2D_DESC,
is_mapped: bool,
}
impl StagingTexture {
pub fn new(device: &ID3D11Device, width: u32, height: u32, format: DXGI_FORMAT) -> Result<Self, Error> {
let desc = D3D11_TEXTURE2D_DESC {
Width: width,
Height: height,
MipLevels: 1,
ArraySize: 1,
Format: format,
SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
Usage: D3D11_USAGE_STAGING,
BindFlags: 0,
CPUAccessFlags: (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32,
MiscFlags: 0,
};
let mut tex = None;
unsafe {
device.CreateTexture2D(&desc, None, Some(&mut tex))?;
}
Ok(Self { inner: tex.unwrap(), desc, is_mapped: false })
}
#[inline]
#[must_use]
pub const fn texture(&self) -> &ID3D11Texture2D {
&self.inner
}
#[inline]
#[must_use]
pub const fn desc(&self) -> D3D11_TEXTURE2D_DESC {
self.desc
}
#[inline]
#[must_use]
pub const fn is_mapped(&self) -> bool {
self.is_mapped
}
#[inline]
pub const fn set_mapped(&mut self, mapped: bool) {
self.is_mapped = mapped;
}
pub fn from_raw_checked(tex: ID3D11Texture2D) -> Option<Self> {
let mut desc = D3D11_TEXTURE2D_DESC::default();
unsafe { tex.GetDesc(&mut desc) };
let is_staging = desc.Usage == D3D11_USAGE_STAGING;
let has_cpu_rw = (desc.CPUAccessFlags & (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32) != 0;
if !is_staging || !has_cpu_rw {
return None;
}
Some(Self { inner: tex, desc, is_mapped: false })
}
}