implement gpu driver version for nvidia

Junkui Zhang created

Change summary

crates/gpui/src/platform/windows/directx_renderer.rs | 116 +++++++++++++
crates/gpui/src/platform/windows/window.rs           |   3 
2 files changed, 112 insertions(+), 7 deletions(-)

Detailed changes

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

@@ -32,6 +32,7 @@ pub(crate) struct DirectXRenderer {
 /// Direct3D objects
 #[derive(Clone)]
 pub(crate) struct DirectXDevices {
+    adapter: IDXGIAdapter1,
     dxgi_factory: IDXGIFactory6,
     #[cfg(not(feature = "enable-renderdoc"))]
     dxgi_device: IDXGIDevice,
@@ -97,6 +98,7 @@ impl DirectXDevices {
         let dxgi_device: IDXGIDevice = device.cast()?;
 
         Ok(Self {
+            adapter,
             dxgi_factory,
             #[cfg(not(feature = "enable-renderdoc"))]
             dxgi_device,
@@ -396,6 +398,34 @@ impl DirectXRenderer {
         }
         Ok(())
     }
+
+    pub(crate) fn gpu_specs(&self) -> Result<GpuSpecs> {
+        let desc = unsafe { self.devices.adapter.GetDesc1() }?;
+        let is_software_emulated = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0;
+        let device_name = String::from_utf16_lossy(&desc.Description)
+            .trim_matches(char::from(0))
+            .to_string();
+        let driver_name = match desc.VendorId {
+            0x10DE => "NVIDIA Corporation".to_string(),
+            0x1002 => "AMD Corporation".to_string(),
+            0x8086 => "Intel Corporation".to_string(),
+            _ => "Unknown Vendor".to_string(),
+        };
+        let driver_version = match desc.VendorId {
+            0x10DE => nvidia::get_driver_version().context("Failed to get NVIDIA driver info"),
+            0x1002 => Err(anyhow::anyhow!("AMD driver info not implemented yet")),
+            0x8086 => Err(anyhow::anyhow!("Intel driver info not implemented yet")),
+            _ => Err(anyhow::anyhow!("Unknown vendor detected.")),
+        }
+        .log_err()
+        .unwrap_or("Unknown Driver".to_string());
+        Ok(GpuSpecs {
+            is_software_emulated,
+            device_name,
+            driver_name,
+            driver_info: driver_version,
+        })
+    }
 }
 
 impl DirectXResources {
@@ -936,12 +966,11 @@ fn get_adapter(dxgi_factory: &IDXGIFactory6) -> Result<IDXGIAdapter1> {
             dxgi_factory
                 .EnumAdapterByGpuPreference(adapter_index, DXGI_GPU_PREFERENCE_MINIMUM_POWER)
         }?;
-        {
-            let desc = unsafe { adapter.GetDesc1() }?;
-            println!(
-                "Select GPU: {}",
-                String::from_utf16_lossy(&desc.Description)
-            );
+        if let Ok(desc) = unsafe { adapter.GetDesc1() } {
+            let gpu_name = String::from_utf16_lossy(&desc.Description)
+                .trim_matches(char::from(0))
+                .to_string();
+            log::info!("Using GPU: {}", gpu_name);
         }
         // Check to see whether the adapter supports Direct3D 11, but don't
         // create the actual device yet.
@@ -1339,3 +1368,78 @@ mod shader_resources {
         }
     }
 }
+
+mod nvidia {
+    use std::os::raw::{c_char, c_int, c_uint};
+
+    use anyhow::{Context, Result};
+    use windows::{
+        Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA},
+        core::s,
+    };
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L180
+    const NVAPI_SHORT_STRING_MAX: usize = 64;
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L235
+    #[allow(non_camel_case_types)]
+    type NvAPI_ShortString = [c_char; NVAPI_SHORT_STRING_MAX];
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi.h#L87
+    #[allow(non_camel_case_types)]
+    type NvAPI_Initialize_t = unsafe extern "C" fn() -> c_int;
+
+    // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_lite_common.h#L447
+    #[allow(non_camel_case_types)]
+    type NvAPI_SYS_GetDriverAndBranchVersion_t = unsafe extern "C" fn(
+        driver_version: *mut c_uint,
+        build_branch_string: *mut NvAPI_ShortString,
+    ) -> c_int;
+
+    pub(super) fn get_driver_version() -> Result<String> {
+        unsafe {
+            // Try to load the NVIDIA driver DLL
+            let nvidia_dll = LoadLibraryA(s!("nvapi64.dll")).context("Can't load nvapi64.dll")?;
+            let nvapi_query_addr = GetProcAddress(nvidia_dll, s!("nvapi_QueryInterface"))
+                .ok_or_else(|| anyhow::anyhow!("Failed to get nvapi_QueryInterface address"))?;
+            let nvapi_query: extern "C" fn(u32) -> *mut () = std::mem::transmute(nvapi_query_addr);
+
+            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L33
+            let nvapi_init_ptr = nvapi_query(0x0150e828);
+            if nvapi_init_ptr.is_null() {
+                anyhow::bail!("Failed to get NVIDIA API function pointer");
+            }
+            let nvapi_init: NvAPI_Initialize_t = std::mem::transmute(nvapi_init_ptr);
+
+            let result = nvapi_init();
+            if result != 0 {
+                anyhow::bail!("Failed to initialize NVIDIA API, error code: {}", result);
+            }
+
+            // https://github.com/NVIDIA/nvapi/blob/7cb76fce2f52de818b3da497af646af1ec16ce27/nvapi_interface.h#L41
+            let nvapi_get_driver_version_ptr = nvapi_query(0x2926aaad);
+            if nvapi_get_driver_version_ptr.is_null() {
+                anyhow::bail!("Failed to get NVIDIA driver version function pointer");
+            }
+            let nvapi_get_driver_version: NvAPI_SYS_GetDriverAndBranchVersion_t =
+                std::mem::transmute(nvapi_get_driver_version_ptr);
+
+            let mut driver_version: c_uint = 0;
+            let mut build_branch_string: NvAPI_ShortString = [0; NVAPI_SHORT_STRING_MAX];
+            let result = nvapi_get_driver_version(
+                &mut driver_version as *mut c_uint,
+                &mut build_branch_string as *mut NvAPI_ShortString,
+            );
+
+            if result != 0 {
+                anyhow::bail!(
+                    "Failed to get NVIDIA driver version, error code: {}",
+                    result
+                );
+            }
+            let major = driver_version / 100;
+            let minor = driver_version % 100;
+            Ok(format!("{}.{}", major, minor))
+        }
+    }
+}

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

@@ -104,6 +104,7 @@ impl WindowsWindowState {
         let restore_from_minimized = None;
         // let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?;
         let renderer = DirectXRenderer::new(gpu_context, hwnd)?;
+        println!("GPU specs: {:#?}", renderer.gpu_specs());
         let callbacks = Callbacks::default();
         let input_handler = None;
         let pending_surrogate = None;
@@ -801,7 +802,7 @@ impl PlatformWindow for WindowsWindow {
     }
 
     fn update_ime_position(&self, _bounds: Bounds<ScaledPixels>) {
-        // todo(windows)
+        // There is no such thing on Windows.
     }
 }