1use windows::Graphics::DirectX::Direct3D11::IDirect3DDevice;
2use windows::Win32::Foundation::HMODULE;
3use windows::Win32::Graphics::Direct3D::{
4 D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_9_1, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_3,
5 D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
6};
7use windows::Win32::Graphics::Direct3D11::{
8 D3D11_CPU_ACCESS_READ, D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION,
9 D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
10};
11use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
12use windows::Win32::Graphics::Dxgi::IDXGIDevice;
13use windows::Win32::System::WinRT::Direct3D11::CreateDirect3D11DeviceFromDXGIDevice;
14use windows::core::Interface;
15
16#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
17pub enum Error {
19 #[error("Failed to create DirectX device with the recommended feature levels")]
21 FeatureLevelNotSatisfied,
22 #[error("Windows API Error: {0}")]
26 WindowsError(#[from] windows::core::Error),
27}
28
29pub struct SendDirectX<T>(pub T);
31
32impl<T> SendDirectX<T> {
33 #[inline]
35 #[must_use]
36 pub const fn new(device: T) -> Self {
37 Self(device)
38 }
39}
40
41#[allow(clippy::non_send_fields_in_send_ty)]
42unsafe impl<T> Send for SendDirectX<T> {}
43
44#[inline]
53pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Error> {
54 let feature_flags = [
59 D3D_FEATURE_LEVEL_11_1,
60 D3D_FEATURE_LEVEL_11_0,
61 D3D_FEATURE_LEVEL_10_1,
62 D3D_FEATURE_LEVEL_10_0,
63 D3D_FEATURE_LEVEL_9_3,
64 D3D_FEATURE_LEVEL_9_2,
65 D3D_FEATURE_LEVEL_9_1,
66 ];
67
68 let mut d3d_device = None;
69 let mut feature_level = D3D_FEATURE_LEVEL::default();
70 let mut d3d_device_context = None;
71 unsafe {
72 D3D11CreateDevice(
73 None,
74 D3D_DRIVER_TYPE_HARDWARE,
75 HMODULE::default(),
76 D3D11_CREATE_DEVICE_BGRA_SUPPORT,
77 Some(&feature_flags),
78 D3D11_SDK_VERSION,
79 Some(&mut d3d_device),
80 Some(&mut feature_level),
81 Some(&mut d3d_device_context),
82 )?;
83 };
84
85 if feature_level.0 < D3D_FEATURE_LEVEL_11_0.0 {
86 return Err(Error::FeatureLevelNotSatisfied);
87 }
88
89 Ok((d3d_device.unwrap(), d3d_device_context.unwrap()))
90}
91
92#[inline]
99pub fn create_direct3d_device(d3d_device: &ID3D11Device) -> Result<IDirect3DDevice, Error> {
100 let dxgi_device: IDXGIDevice = d3d_device.cast()?;
101 let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device)? };
102 let device: IDirect3DDevice = inspectable.cast()?;
103
104 Ok(device)
105}
106
107pub struct StagingTexture {
109 inner: ID3D11Texture2D,
110 desc: D3D11_TEXTURE2D_DESC,
111 is_mapped: bool,
112}
113
114impl StagingTexture {
115 pub fn new(device: &ID3D11Device, width: u32, height: u32, format: DXGI_FORMAT) -> Result<Self, Error> {
117 let desc = D3D11_TEXTURE2D_DESC {
118 Width: width,
119 Height: height,
120 MipLevels: 1,
121 ArraySize: 1,
122 Format: format,
123 SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
124 Usage: D3D11_USAGE_STAGING,
125 BindFlags: 0,
126 CPUAccessFlags: (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32,
127 MiscFlags: 0,
128 };
129
130 let mut tex = None;
131 unsafe {
132 device.CreateTexture2D(&desc, None, Some(&mut tex))?;
133 }
134 Ok(Self { inner: tex.unwrap(), desc, is_mapped: false })
135 }
136
137 #[inline]
139 #[must_use]
140 pub const fn texture(&self) -> &ID3D11Texture2D {
141 &self.inner
142 }
143
144 #[inline]
146 #[must_use]
147 pub const fn desc(&self) -> D3D11_TEXTURE2D_DESC {
148 self.desc
149 }
150
151 #[inline]
153 #[must_use]
154 pub const fn is_mapped(&self) -> bool {
155 self.is_mapped
156 }
157
158 #[inline]
160 pub const fn set_mapped(&mut self, mapped: bool) {
161 self.is_mapped = mapped;
162 }
163
164 pub fn from_raw_checked(tex: ID3D11Texture2D) -> Option<Self> {
168 let mut desc = D3D11_TEXTURE2D_DESC::default();
169 unsafe { tex.GetDesc(&mut desc) };
170 let is_staging = desc.Usage == D3D11_USAGE_STAGING;
171 let has_cpu_rw = (desc.CPUAccessFlags & (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32) != 0;
172
173 if !is_staging || !has_cpu_rw {
174 return None;
175 }
176
177 Some(Self { inner: tex, desc, is_mapped: false })
178 }
179}