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