1use anyhow::{Context, Result};
2use util::ResultExt;
3use windows::Win32::{
4 Foundation::HMODULE,
5 Graphics::{
6 Direct3D::{
7 D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL, D3D_FEATURE_LEVEL_10_1,
8 D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
9 },
10 Direct3D11::{
11 D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG,
12 D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS,
13 D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
14 },
15 Dxgi::{
16 CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
17 IDXGIAdapter1, IDXGIFactory6,
18 },
19 },
20};
21use windows::core::Interface;
22
23pub(crate) fn try_to_recover_from_device_lost<T>(
24 mut f: impl FnMut() -> Result<T>,
25 on_success: impl FnOnce(T),
26 on_error: impl FnOnce(),
27) {
28 let result = (0..5).find_map(|i| {
29 if i > 0 {
30 // Add a small delay before retrying
31 std::thread::sleep(std::time::Duration::from_millis(100));
32 }
33 f().log_err()
34 });
35
36 if let Some(result) = result {
37 on_success(result);
38 } else {
39 on_error();
40 }
41}
42
43#[derive(Clone)]
44pub(crate) struct DirectXDevices {
45 pub(crate) adapter: IDXGIAdapter1,
46 pub(crate) dxgi_factory: IDXGIFactory6,
47 pub(crate) device: ID3D11Device,
48 pub(crate) device_context: ID3D11DeviceContext,
49}
50
51impl DirectXDevices {
52 pub(crate) fn new() -> Result<Self> {
53 let debug_layer_available = check_debug_layer_available();
54 let dxgi_factory =
55 get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
56 let adapter =
57 get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
58 let (device, device_context) = {
59 let mut context: Option<ID3D11DeviceContext> = None;
60 let mut feature_level = D3D_FEATURE_LEVEL::default();
61 let device = get_device(
62 &adapter,
63 Some(&mut context),
64 Some(&mut feature_level),
65 debug_layer_available,
66 )
67 .context("Creating Direct3D device")?;
68 match feature_level {
69 D3D_FEATURE_LEVEL_11_1 => {
70 log::info!("Created device with Direct3D 11.1 feature level.")
71 }
72 D3D_FEATURE_LEVEL_11_0 => {
73 log::info!("Created device with Direct3D 11.0 feature level.")
74 }
75 D3D_FEATURE_LEVEL_10_1 => {
76 log::info!("Created device with Direct3D 10.1 feature level.")
77 }
78 _ => unreachable!(),
79 }
80 (device, context.unwrap())
81 };
82
83 Ok(Self {
84 adapter,
85 dxgi_factory,
86 device,
87 device_context,
88 })
89 }
90}
91
92#[inline]
93fn check_debug_layer_available() -> bool {
94 #[cfg(debug_assertions)]
95 {
96 use windows::Win32::Graphics::Dxgi::{DXGIGetDebugInterface1, IDXGIInfoQueue};
97
98 unsafe { DXGIGetDebugInterface1::<IDXGIInfoQueue>(0) }
99 .log_err()
100 .is_some()
101 }
102 #[cfg(not(debug_assertions))]
103 {
104 false
105 }
106}
107
108#[inline]
109fn get_dxgi_factory(debug_layer_available: bool) -> Result<IDXGIFactory6> {
110 let factory_flag = if debug_layer_available {
111 DXGI_CREATE_FACTORY_DEBUG
112 } else {
113 #[cfg(debug_assertions)]
114 log::warn!(
115 "Failed to get DXGI debug interface. DirectX debugging features will be disabled."
116 );
117 DXGI_CREATE_FACTORY_FLAGS::default()
118 };
119 unsafe { Ok(CreateDXGIFactory2(factory_flag)?) }
120}
121
122#[inline]
123fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Result<IDXGIAdapter1> {
124 for adapter_index in 0.. {
125 let adapter: IDXGIAdapter1 = unsafe { dxgi_factory.EnumAdapters(adapter_index)?.cast()? };
126 if let Ok(desc) = unsafe { adapter.GetDesc1() } {
127 let gpu_name = String::from_utf16_lossy(&desc.Description)
128 .trim_matches(char::from(0))
129 .to_string();
130 log::info!("Using GPU: {}", gpu_name);
131 }
132 // Check to see whether the adapter supports Direct3D 11, but don't
133 // create the actual device yet.
134 if get_device(&adapter, None, None, debug_layer_available)
135 .log_err()
136 .is_some()
137 {
138 return Ok(adapter);
139 }
140 }
141
142 unreachable!()
143}
144
145#[inline]
146fn get_device(
147 adapter: &IDXGIAdapter1,
148 context: Option<*mut Option<ID3D11DeviceContext>>,
149 feature_level: Option<*mut D3D_FEATURE_LEVEL>,
150 debug_layer_available: bool,
151) -> Result<ID3D11Device> {
152 let mut device: Option<ID3D11Device> = None;
153 let device_flags = if debug_layer_available {
154 D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
155 } else {
156 D3D11_CREATE_DEVICE_BGRA_SUPPORT
157 };
158 unsafe {
159 D3D11CreateDevice(
160 adapter,
161 D3D_DRIVER_TYPE_UNKNOWN,
162 HMODULE::default(),
163 device_flags,
164 // 4x MSAA is required for Direct3D Feature Level 10.1 or better
165 Some(&[
166 D3D_FEATURE_LEVEL_11_1,
167 D3D_FEATURE_LEVEL_11_0,
168 D3D_FEATURE_LEVEL_10_1,
169 ]),
170 D3D11_SDK_VERSION,
171 Some(&mut device),
172 feature_level,
173 context,
174 )?;
175 }
176 let device = device.unwrap();
177 let mut data = D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS::default();
178 unsafe {
179 device
180 .CheckFeatureSupport(
181 D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS,
182 &mut data as *mut _ as _,
183 std::mem::size_of::<D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS>() as u32,
184 )
185 .context("Checking GPU device feature support")?;
186 }
187 if data
188 .ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
189 .as_bool()
190 {
191 Ok(device)
192 } else {
193 Err(anyhow::anyhow!(
194 "Required feature StructuredBuffer is not supported by GPU/driver"
195 ))
196 }
197}