1use std::slice;
2
3use windows::Graphics::DirectX::Direct3D11::IDirect3DDevice;
4use windows::Win32::Foundation::HMODULE;
5use windows::Win32::Graphics::Direct3D::{
6 D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_9_1, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_3,
7 D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
8};
9use windows::Win32::Graphics::Direct3D11::{
10 D3D11_CPU_ACCESS_READ, D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_MAP_READ_WRITE,
11 D3D11_MAPPED_SUBRESOURCE, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, D3D11CreateDevice,
12 ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
13};
14use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT, DXGI_SAMPLE_DESC};
15use windows::Win32::Graphics::Dxgi::IDXGIDevice;
16use windows::Win32::System::WinRT::Direct3D11::CreateDirect3D11DeviceFromDXGIDevice;
17use windows::core::Interface;
18
19#[derive(thiserror::Error, Eq, PartialEq, Clone, Debug)]
20pub enum Error {
22 #[error("Failed to create DirectX device with the recommended feature levels")]
24 FeatureLevelNotSatisfied,
25 #[error("Windows API succeeded but did not return {0}")]
27 UnexpectedNullResult(&'static str),
28 #[error("Windows API Error: {0}")]
32 WindowsError(#[from] windows::core::Error),
33}
34
35pub struct SendDirectX<T>(pub T);
37
38impl<T> SendDirectX<T> {
39 #[inline]
41 #[must_use]
42 pub const fn new(device: T) -> Self {
43 Self(device)
44 }
45}
46
47#[allow(clippy::non_send_fields_in_send_ty)]
48unsafe impl<T> Send for SendDirectX<T> {}
49
50enum StagingTextureHandle<'a> {
51 Owned(StagingTexture),
52 Borrowed(&'a mut StagingTexture),
53}
54
55impl StagingTextureHandle<'_> {
56 const fn texture(&self) -> &ID3D11Texture2D {
57 match self {
58 Self::Owned(texture) => texture.texture(),
59 Self::Borrowed(texture) => texture.texture(),
60 }
61 }
62
63 const fn set_mapped(&mut self, mapped: bool) {
64 match self {
65 Self::Owned(texture) => texture.set_mapped(mapped),
66 Self::Borrowed(texture) => texture.set_mapped(mapped),
67 }
68 }
69}
70
71pub(crate) struct MappedStagingTexture<'a> {
73 context: &'a ID3D11DeviceContext,
74 texture: StagingTextureHandle<'a>,
75 mapped: D3D11_MAPPED_SUBRESOURCE,
76}
77
78impl<'a> MappedStagingTexture<'a> {
79 pub fn map_owned(context: &'a ID3D11DeviceContext, texture: StagingTexture) -> Result<Self, windows::core::Error> {
81 Self::map(context, StagingTextureHandle::Owned(texture))
82 }
83
84 pub fn map_borrowed(
86 context: &'a ID3D11DeviceContext,
87 texture: &'a mut StagingTexture,
88 ) -> Result<Self, windows::core::Error> {
89 unmap_staging_texture(context, texture);
90 Self::map(context, StagingTextureHandle::Borrowed(texture))
91 }
92
93 fn map(
94 context: &'a ID3D11DeviceContext,
95 mut texture: StagingTextureHandle<'a>,
96 ) -> Result<Self, windows::core::Error> {
97 let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
98 unsafe {
99 context.Map(texture.texture(), 0, D3D11_MAP_READ_WRITE, 0, Some(&mut mapped))?;
100 }
101 texture.set_mapped(true);
102
103 Ok(Self { context, texture, mapped })
104 }
105
106 #[must_use]
108 pub const fn as_slice(&self, rows: u32) -> &[u8] {
109 let len = rows as usize * self.mapped.RowPitch as usize;
110 unsafe { slice::from_raw_parts(self.mapped.pData.cast(), len) }
111 }
112
113 #[must_use]
115 pub const fn as_mut_slice(&mut self, rows: u32) -> &mut [u8] {
116 let len = rows as usize * self.mapped.RowPitch as usize;
117 unsafe { slice::from_raw_parts_mut(self.mapped.pData.cast(), len) }
118 }
119
120 #[must_use]
122 pub const fn row_pitch(&self) -> u32 {
123 self.mapped.RowPitch
124 }
125
126 #[must_use]
128 pub const fn depth_pitch(&self) -> u32 {
129 self.mapped.DepthPitch
130 }
131}
132
133impl Drop for MappedStagingTexture<'_> {
134 fn drop(&mut self) {
135 unsafe {
136 self.context.Unmap(self.texture.texture(), 0);
137 }
138 self.texture.set_mapped(false);
139 }
140}
141
142pub(crate) fn unmap_staging_texture(context: &ID3D11DeviceContext, texture: &mut StagingTexture) {
144 if texture.is_mapped() {
145 unsafe {
146 context.Unmap(texture.texture(), 0);
147 }
148 texture.set_mapped(false);
149 }
150}
151
152#[inline]
161pub fn create_d3d_device() -> Result<(ID3D11Device, ID3D11DeviceContext), Error> {
162 let feature_flags = [
167 D3D_FEATURE_LEVEL_11_1,
168 D3D_FEATURE_LEVEL_11_0,
169 D3D_FEATURE_LEVEL_10_1,
170 D3D_FEATURE_LEVEL_10_0,
171 D3D_FEATURE_LEVEL_9_3,
172 D3D_FEATURE_LEVEL_9_2,
173 D3D_FEATURE_LEVEL_9_1,
174 ];
175
176 let mut d3d_device = None;
177 let mut feature_level = D3D_FEATURE_LEVEL::default();
178 let mut d3d_device_context = None;
179 unsafe {
180 D3D11CreateDevice(
181 None,
182 D3D_DRIVER_TYPE_HARDWARE,
183 HMODULE::default(),
184 D3D11_CREATE_DEVICE_BGRA_SUPPORT,
185 Some(&feature_flags),
186 D3D11_SDK_VERSION,
187 Some(&mut d3d_device),
188 Some(&mut feature_level),
189 Some(&mut d3d_device_context),
190 )?;
191 };
192
193 if feature_level.0 < D3D_FEATURE_LEVEL_11_0.0 {
194 return Err(Error::FeatureLevelNotSatisfied);
195 }
196
197 let d3d_device = d3d_device.ok_or(Error::UnexpectedNullResult("an `ID3D11Device`"))?;
198 let d3d_device_context = d3d_device_context.ok_or(Error::UnexpectedNullResult("an `ID3D11DeviceContext`"))?;
199
200 Ok((d3d_device, d3d_device_context))
201}
202
203#[inline]
210pub fn create_direct3d_device(d3d_device: &ID3D11Device) -> Result<IDirect3DDevice, Error> {
211 let dxgi_device: IDXGIDevice = d3d_device.cast()?;
212 let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device)? };
213 let device: IDirect3DDevice = inspectable.cast()?;
214
215 Ok(device)
216}
217
218pub struct StagingTexture {
220 inner: ID3D11Texture2D,
221 desc: D3D11_TEXTURE2D_DESC,
222 is_mapped: bool,
223}
224
225impl StagingTexture {
226 pub fn new(device: &ID3D11Device, width: u32, height: u32, format: DXGI_FORMAT) -> Result<Self, Error> {
228 let desc = D3D11_TEXTURE2D_DESC {
229 Width: width,
230 Height: height,
231 MipLevels: 1,
232 ArraySize: 1,
233 Format: format,
234 SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
235 Usage: D3D11_USAGE_STAGING,
236 BindFlags: 0,
237 CPUAccessFlags: (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32,
238 MiscFlags: 0,
239 };
240
241 let mut tex = None;
242 unsafe {
243 device.CreateTexture2D(&desc, None, Some(&mut tex))?;
244 }
245 let inner = tex.ok_or(Error::UnexpectedNullResult("an `ID3D11Texture2D`"))?;
246
247 Ok(Self { inner, desc, is_mapped: false })
248 }
249
250 #[inline]
252 #[must_use]
253 pub const fn texture(&self) -> &ID3D11Texture2D {
254 &self.inner
255 }
256
257 #[inline]
259 #[must_use]
260 pub const fn desc(&self) -> D3D11_TEXTURE2D_DESC {
261 self.desc
262 }
263
264 #[inline]
266 #[must_use]
267 pub const fn is_mapped(&self) -> bool {
268 self.is_mapped
269 }
270
271 #[inline]
273 pub const fn set_mapped(&mut self, mapped: bool) {
274 self.is_mapped = mapped;
275 }
276
277 pub fn from_raw_checked(tex: ID3D11Texture2D) -> Option<Self> {
281 let mut desc = D3D11_TEXTURE2D_DESC::default();
282 unsafe { tex.GetDesc(&mut desc) };
283 let is_staging = desc.Usage == D3D11_USAGE_STAGING;
284 let cpu_rw_mask = (D3D11_CPU_ACCESS_READ.0 | D3D11_CPU_ACCESS_WRITE.0) as u32;
285 let has_cpu_rw = (desc.CPUAccessFlags & cpu_rw_mask) == cpu_rw_mask;
286
287 if !is_staging || !has_cpu_rw {
288 return None;
289 }
290
291 Some(Self { inner: tex, desc, is_mapped: false })
292 }
293}