piet_direct2d/
d3d.rs

1// Copyright 2020 the Piet Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use std::ptr::null_mut;
5
6// TODO figure out whether to export this or to move `raw_pixels` into this module.
7pub use winapi::shared::dxgi::DXGI_MAP_READ;
8
9use winapi::Interface;
10use winapi::shared::dxgi::{IDXGIDevice, IDXGISurface};
11use winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM;
12use winapi::shared::dxgitype::DXGI_SAMPLE_DESC;
13use winapi::shared::winerror::{HRESULT, SUCCEEDED};
14use winapi::um::d3d11::{
15    D3D11_BIND_FLAG, D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CPU_ACCESS_FLAG,
16    D3D11_CPU_ACCESS_READ, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION,
17    D3D11_TEXTURE2D_DESC, D3D11_USAGE, D3D11_USAGE_DEFAULT, D3D11_USAGE_STAGING, D3D11CreateDevice,
18    ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
19};
20use winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE;
21
22use wio::com::ComPtr;
23
24#[derive(Debug)]
25pub struct Error(HRESULT);
26
27pub struct D3D11Device(ComPtr<ID3D11Device>);
28pub struct D3D11DeviceContext(ComPtr<ID3D11DeviceContext>);
29pub struct D3D11Texture2D(ComPtr<ID3D11Texture2D>);
30pub struct DxgiDevice(ComPtr<IDXGIDevice>);
31
32// According to Microsoft's docs, D3D11Device does its own synchronization. It also says that "only
33// one thread can call a ID3D11DeviceContext at a time", so D3D11DeviceContext is definitely not
34// Sync, but probably Send (or else they would have said something).
35//
36// https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-render-multi-thread-intro
37unsafe impl Send for D3D11Device {}
38unsafe impl Sync for D3D11Device {}
39unsafe impl Send for D3D11DeviceContext {}
40
41pub enum TextureMode {
42    Target,
43    Read,
44}
45
46impl TextureMode {
47    pub fn usage(&self) -> D3D11_USAGE {
48        match self {
49            TextureMode::Target => D3D11_USAGE_DEFAULT,
50            TextureMode::Read => D3D11_USAGE_STAGING,
51        }
52    }
53
54    pub fn bind_flags(&self) -> D3D11_BIND_FLAG {
55        match self {
56            TextureMode::Target => D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
57            TextureMode::Read => 0,
58        }
59    }
60
61    pub fn cpu_access_flags(&self) -> D3D11_CPU_ACCESS_FLAG {
62        match self {
63            TextureMode::Target => 0,
64            TextureMode::Read => D3D11_CPU_ACCESS_READ,
65        }
66    }
67}
68
69unsafe fn wrap<T, U, F>(hr: HRESULT, ptr: *mut T, f: F) -> Result<U, Error>
70where
71    F: Fn(ComPtr<T>) -> U,
72    T: Interface,
73{
74    if SUCCEEDED(hr) {
75        Ok(f(unsafe { ComPtr::from_raw(ptr) }))
76    } else {
77        Err(Error(hr))
78    }
79}
80
81impl D3D11Device {
82    pub fn inner(&self) -> &ComPtr<ID3D11Device> {
83        &self.0
84    }
85
86    // This function only supports a fraction of available options.
87    pub fn create() -> Result<(D3D11Device, D3D11DeviceContext), Error> {
88        unsafe {
89            let mut ptr = null_mut();
90            let mut ctx_ptr = null_mut();
91            let hr = D3D11CreateDevice(
92                null_mut(), /* adapter */
93                D3D_DRIVER_TYPE_HARDWARE,
94                null_mut(), /* module */
95                D3D11_CREATE_DEVICE_BGRA_SUPPORT,
96                null_mut(), /* feature levels */
97                0,
98                D3D11_SDK_VERSION,
99                &mut ptr,
100                null_mut(), /* feature level */
101                &mut ctx_ptr,
102            );
103            let device = wrap(hr, ptr, D3D11Device)?;
104            let device_ctx = wrap(hr, ctx_ptr, D3D11DeviceContext)?;
105            Ok((device, device_ctx))
106        }
107    }
108
109    pub fn as_dxgi(&self) -> Option<DxgiDevice> {
110        self.0.cast().ok().map(DxgiDevice)
111    }
112
113    pub fn create_texture(
114        &self,
115        width: u32,
116        height: u32,
117        mode: TextureMode,
118    ) -> Result<D3D11Texture2D, Error> {
119        unsafe {
120            let mut ptr = null_mut();
121            let desc = D3D11_TEXTURE2D_DESC {
122                Width: width,
123                Height: height,
124                MipLevels: 1,
125                ArraySize: 1,
126                Format: DXGI_FORMAT_R8G8B8A8_UNORM,
127                SampleDesc: DXGI_SAMPLE_DESC {
128                    Count: 1,
129                    Quality: 0,
130                },
131                Usage: mode.usage(),
132                BindFlags: mode.bind_flags(),
133                CPUAccessFlags: mode.cpu_access_flags(),
134                MiscFlags: 0,
135            };
136            let hr = self.0.CreateTexture2D(&desc, null_mut(), &mut ptr);
137            wrap(hr, ptr, D3D11Texture2D)
138        }
139    }
140}
141
142impl D3D11DeviceContext {
143    pub fn inner(&self) -> &ComPtr<ID3D11DeviceContext> {
144        &self.0
145    }
146}
147
148impl D3D11Texture2D {
149    pub fn as_dxgi(&self) -> ComPtr<IDXGISurface> {
150        self.0.cast().unwrap()
151    }
152
153    pub fn as_raw(&self) -> *mut ID3D11Texture2D {
154        self.0.as_raw()
155    }
156}
157
158impl DxgiDevice {
159    pub fn as_raw(&self) -> *mut IDXGIDevice {
160        self.0.as_raw()
161    }
162}
163
164impl std::fmt::Display for Error {
165    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
166        write!(f, "HRESULT error {}", self.0)
167    }
168}
169
170impl std::error::Error for Error {}