windows: Check required GPU/driver feature `StructuredBuffer` (#37776)

张小白 created

Check whether the GPU/driver supports the StructuredBuffer feature
required by our shaders. If it doesn’t, log an error and skip that
GPU/driver, so Windows can fall back to the software renderer.

Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/directx_devices.rs | 41 ++++++++++----
1 file changed, 30 insertions(+), 11 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/directx_devices.rs 🔗

@@ -8,8 +8,9 @@ use windows::Win32::{
             D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_1,
         },
         Direct3D11::{
-            D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG, D3D11_SDK_VERSION,
-            D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
+            D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_DEBUG,
+            D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS,
+            D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
         },
         Dxgi::{
             CreateDXGIFactory2, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS,
@@ -54,12 +55,10 @@ impl DirectXDevices {
         let adapter =
             get_adapter(&dxgi_factory, debug_layer_available).context("Getting DXGI adapter")?;
         let (device, device_context) = {
-            let mut device: Option<ID3D11Device> = None;
             let mut context: Option<ID3D11DeviceContext> = None;
             let mut feature_level = D3D_FEATURE_LEVEL::default();
-            get_device(
+            let device = get_device(
                 &adapter,
-                Some(&mut device),
                 Some(&mut context),
                 Some(&mut feature_level),
                 debug_layer_available,
@@ -77,7 +76,7 @@ impl DirectXDevices {
                 }
                 _ => unreachable!(),
             }
-            (device.unwrap(), context.unwrap())
+            (device, context.unwrap())
         };
 
         Ok(Self {
@@ -134,7 +133,7 @@ fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Res
         }
         // Check to see whether the adapter supports Direct3D 11, but don't
         // create the actual device yet.
-        if get_device(&adapter, None, None, None, debug_layer_available)
+        if get_device(&adapter, None, None, debug_layer_available)
             .log_err()
             .is_some()
         {
@@ -148,11 +147,11 @@ fn get_adapter(dxgi_factory: &IDXGIFactory6, debug_layer_available: bool) -> Res
 #[inline]
 fn get_device(
     adapter: &IDXGIAdapter1,
-    device: Option<*mut Option<ID3D11Device>>,
     context: Option<*mut Option<ID3D11DeviceContext>>,
     feature_level: Option<*mut D3D_FEATURE_LEVEL>,
     debug_layer_available: bool,
-) -> Result<()> {
+) -> Result<ID3D11Device> {
+    let mut device: Option<ID3D11Device> = None;
     let device_flags = if debug_layer_available {
         D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG
     } else {
@@ -171,10 +170,30 @@ fn get_device(
                 D3D_FEATURE_LEVEL_10_1,
             ]),
             D3D11_SDK_VERSION,
-            device,
+            Some(&mut device),
             feature_level,
             context,
         )?;
     }
-    Ok(())
+    let device = device.unwrap();
+    let mut data = D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS::default();
+    unsafe {
+        device
+            .CheckFeatureSupport(
+                D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS,
+                &mut data as *mut _ as _,
+                std::mem::size_of::<D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS>() as u32,
+            )
+            .context("Checking GPU device feature support")?;
+    }
+    if data
+        .ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x
+        .as_bool()
+    {
+        Ok(device)
+    } else {
+        Err(anyhow::anyhow!(
+            "Required feature StructuredBuffer is not supported by GPU/driver"
+        ))
+    }
 }