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            IDXGIAdapter1, IDXGIFactory6,
 18        },
 19    },
 20};
 21use windows::core::Interface;
 22
 23pub(crate) fn try_to_recover_from_device_lost<T>(
 24    mut f: impl FnMut() -> Result<T>,
 25    on_success: impl FnOnce(T),
 26    on_error: impl FnOnce(),
 27) {
 28    let result = (0..5).find_map(|i| {
 29        if i > 0 {
 30            // Add a small delay before retrying
 31            std::thread::sleep(std::time::Duration::from_millis(100));
 32        }
 33        f().log_err()
 34    });
 35
 36    if let Some(result) = result {
 37        on_success(result);
 38    } else {
 39        on_error();
 40    }
 41}
 42
 43#[derive(Clone)]
 44pub(crate) struct DirectXDevices {
 45    pub(crate) adapter: IDXGIAdapter1,
 46    pub(crate) dxgi_factory: IDXGIFactory6,
 47    pub(crate) device: ID3D11Device,
 48    pub(crate) device_context: ID3D11DeviceContext,
 49}
 50
 51impl DirectXDevices {
 52    pub(crate) fn new() -> Result<Self> {
 53        let debug_layer_available = check_debug_layer_available();
 54        let dxgi_factory =
 55            get_dxgi_factory(debug_layer_available).context("Creating DXGI factory")?;
 56        let adapter =
 57            get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
 58        let (device, device_context) = {
 59            let mut context: Option<ID3D11DeviceContext> = None;
 60            let mut feature_level = D3D_FEATURE_LEVEL::default();
 61            let device = get_device(
 62                &adapter,
 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, 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 { dxgi_factory.EnumAdapters(adapter_index)?.cast()? };
126        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
127            let gpu_name = String::from_utf16_lossy(&desc.Description)
128                .trim_matches(char::from(0))
129                .to_string();
130            log::info!("Using GPU: {}", gpu_name);
131        }
132        // Check to see whether the adapter supports Direct3D 11, but don't
133        // create the actual device yet.
134        if get_device(&adapter, None, None, debug_layer_available)
135            .log_err()
136            .is_some()
137        {
138            return Ok(adapter);
139        }
140    }
141
142    unreachable!()
143}
144
145#[inline]
146fn get_device(
147    adapter: &IDXGIAdapter1,
148    context: Option<*mut Option<ID3D11DeviceContext>>,
149    feature_level: Option<*mut D3D_FEATURE_LEVEL>,
150    debug_layer_available: bool,
151) -> Result<ID3D11Device> {
152    let mut device: Option<ID3D11Device> = None;
153    let device_flags = if debug_layer_available {
154        D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
155    } else {
156        D3D11_CREATE_DEVICE_BGRA_SUPPORT
157    };
158    unsafe {
159        D3D11CreateDevice(
160            adapter,
161            D3D_DRIVER_TYPE_UNKNOWN,
162            HMODULE::default(),
163            device_flags,
164            // 4x MSAA is required for Direct3D Feature Level 10.1 or better
165            Some(&[
166                D3D_FEATURE_LEVEL_11_1,
167                D3D_FEATURE_LEVEL_11_0,
168                D3D_FEATURE_LEVEL_10_1,
169            ]),
170            D3D11_SDK_VERSION,
171            Some(&mut device),
172            feature_level,
173            context,
174        )?;
175    }
176    let device = device.unwrap();
177    let mut data = D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS::default();
178    unsafe {
179        device
180            .CheckFeatureSupport(
181                D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS,
182                &mut data as *mut _ as _,
183                std::mem::size_of::<D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS>() as u32,
184            )
185            .context("Checking GPU device feature support")?;
186    }
187    if data
188        .ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
189        .as_bool()
190    {
191        Ok(device)
192    } else {
193        Err(anyhow::anyhow!(
194            "Required feature StructuredBuffer is not supported by GPU/driver"
195        ))
196    }
197}