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, D3D11_SDK_VERSION,
12 D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
13 },
14 Dxgi::{
15 CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
16 DXGI_GPU_PREFERENCE_MINIMUM_POWER, IDXGIAdapter1, IDXGIFactory6,
17 },
18 },
19};
20
21pub(crate) fn try_to_recover_from_device_lost<T>(
22 mut f: impl FnMut() -> Result<T>,
23 on_success: impl FnOnce(T),
24 on_error: impl FnOnce(),
25) {
26 let result = (0..5).find_map(|i| {
27 if i > 0 {
28 // Add a small delay before retrying
29 std::thread::sleep(std::time::Duration::from_millis(100));
30 }
31 f().log_err()
32 });
33
34 if let Some(result) = result {
35 on_success(result);
36 } else {
37 on_error();
38 }
39}
40
41#[derive(Clone)]
42pub(crate) struct DirectXDevices {
43 pub(crate) adapter: IDXGIAdapter1,
44 pub(crate) dxgi_factory: IDXGIFactory6,
45 pub(crate) device: ID3D11Device,
46 pub(crate) device_context: ID3D11DeviceContext,
47}
48
49impl DirectXDevices {
50 pub(crate) fn new() -> Result<Self> {
51 let debug_layer_available = check_debug_layer_available();
52 let dxgi_factory =
53 get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
54 let adapter =
55 get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
56 let (device, device_context) = {
57 let mut device: Option<ID3D11Device> = None;
58 let mut context: Option<ID3D11DeviceContext> = None;
59 let mut feature_level = D3D_FEATURE_LEVEL::default();
60 get_device(
61 &adapter,
62 Some(&mut device),
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.unwrap(), 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 {
126 dxgi_factory
127 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
128 }?;
129 if let Ok(desc) = unsafe { adapter.GetDesc1() } {
130 let gpu_name = String::from_utf16_lossy(&desc.Description)
131 .trim_matches(char::from(0))
132 .to_string();
133 log::info!("Using GPU: {}", gpu_name);
134 }
135 // Check to see whether the adapter supports Direct3D 11, but don't
136 // create the actual device yet.
137 if get_device(&adapter, None, None, None, debug_layer_available)
138 .log_err()
139 .is_some()
140 {
141 return Ok(adapter);
142 }
143 }
144
145 unreachable!()
146}
147
148#[inline]
149fn get_device(
150 adapter: &IDXGIAdapter1,
151 device: Option<*mut Option<ID3D11Device>>,
152 context: Option<*mut Option<ID3D11DeviceContext>>,
153 feature_level: Option<*mut D3D_FEATURE_LEVEL>,
154 debug_layer_available: bool,
155) -> Result<()> {
156 let device_flags = if debug_layer_available {
157 D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
158 } else {
159 D3D11_CREATE_DEVICE_BGRA_SUPPORT
160 };
161 unsafe {
162 D3D11CreateDevice(
163 adapter,
164 D3D_DRIVER_TYPE_UNKNOWN,
165 HMODULE::default(),
166 device_flags,
167 // 4x MSAA is required for Direct3D Feature Level 10.1 or better
168 Some(&[
169 D3D_FEATURE_LEVEL_11_1,
170 D3D_FEATURE_LEVEL_11_0,
171 D3D_FEATURE_LEVEL_10_1,
172 ]),
173 D3D11_SDK_VERSION,
174 device,
175 feature_level,
176 context,
177 )?;
178 }
179 Ok(())
180}