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