directx_devices.rs

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