directx_devices.rs

  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}