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, 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}