From c2287b1d5c785d6cd505ef3c27c4d3197bd72a25 Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 26 Sep 2025 00:28:42 -0700 Subject: [PATCH] windows vram display in about, better rocm display on windows, amd/nvidia driver display --- src/hooks/useWarnings.ts | 11 ++-- src/main/modules/hardware.ts | 122 +++++++++++++++++++++++++---------- src/types/hardware.d.ts | 2 + src/utils/node/gpu.ts | 60 ++++++++++++++++- src/utils/systemInfo.ts | 22 ++++++- 5 files changed, 173 insertions(+), 44 deletions(-) diff --git a/src/hooks/useWarnings.ts b/src/hooks/useWarnings.ts index 6dd9f86..b3fcf85 100644 --- a/src/hooks/useWarnings.ts +++ b/src/hooks/useWarnings.ts @@ -108,13 +108,14 @@ const checkVramWarnings = async (backend: string): Promise => { if (gpuMemoryInfo) { const lowVramThreshold = 8; - const lowVramGpus = gpuMemoryInfo.filter( - (gpu) => - typeof gpu.totalMemoryGB === 'number' && - gpu.totalMemoryGB < lowVramThreshold + const validGpus = gpuMemoryInfo.filter( + (gpu) => typeof gpu.totalMemoryGB === 'number' + ); + const lowVramGpus = validGpus.filter( + (gpu) => gpu.totalMemoryGB! < lowVramThreshold ); - if (lowVramGpus.length > 0) { + if (validGpus.length > 0 && lowVramGpus.length === validGpus.length) { const memoryDetails = lowVramGpus .map((gpu) => `${gpu.totalMemoryGB!.toFixed(1)}GB`) .join(', '); diff --git a/src/main/modules/hardware.ts b/src/main/modules/hardware.ts index 43233a5..3d2eddd 100644 --- a/src/main/modules/hardware.ts +++ b/src/main/modules/hardware.ts @@ -28,7 +28,7 @@ export async function detectCPU() { const devices: string[] = []; if (cpu.brand) { devices.push( - `${formatDeviceName(cpu.brand)} (${cpu.physicalCores} cores, ${cpu.speed} GHz)` + `${formatDeviceName(cpu.brand)} (${cpu.cores} cores) @ ${cpu.speed} GHz` ); } @@ -125,17 +125,12 @@ export async function detectGPUCapabilities() { async function detectCUDA() { try { - const { stdout } = await execa( - 'nvidia-smi', - ['--query-gpu=name,driver_version', '--format=csv,noheader,nounits'], - { - timeout: 5000, - reject: false, - } - ); + const { stdout } = await execa('nvidia-smi', [], { + timeout: 5000, + reject: false, + }); if (stdout.trim()) { - // Check for error messages that indicate nvidia-smi failed const errorPatterns = [ 'NVIDIA-SMI has failed', 'No devices found', @@ -152,24 +147,39 @@ async function detectCUDA() { return { supported: false, devices: [] } as const; } - const lines = stdout.trim().split('\n'); const devices: string[] = []; - let version: string | undefined; + let cudaVersion: string | undefined; + let driverVersion: string | undefined; - for (const line of lines) { - const parts = line.split(','); - const rawName = parts[0]?.trim() || 'Unknown NVIDIA GPU'; - devices.push(formatDeviceName(rawName)); + const cudaMatch = stdout.match(/CUDA Version:\s*(\d+\.\d+)/); + if (cudaMatch) { + cudaVersion = cudaMatch[1]; + } - if (!version && parts[1]?.trim()) { - version = parts[1].trim(); + const driverMatch = stdout.match( + /Driver Version:\s*(\d+\.\d+(?:\.\d+)?)/ + ); + if (driverMatch) { + driverVersion = driverMatch[1]; + } + + const gpuNameMatch = stdout.match(/\|\s+\d+\s+([^|]+)\s+On\s+\|/g); + if (gpuNameMatch) { + for (const match of gpuNameMatch) { + const name = match + .replace(/\|\s+\d+\s+([^|]+)\s+On\s+\|/, '$1') + .trim(); + if (name) { + devices.push(formatDeviceName(name)); + } } } return { - supported: devices.length > 0, + supported: devices.length > 0 || !!cudaVersion, devices, - version, + version: cudaVersion, + driverVersion, } as const; } @@ -245,27 +255,69 @@ export async function detectROCm() { } let version: string | undefined; - try { - const { stdout: amdSmiOutput } = await execa('amd-smi', { - timeout: 3000, - reject: false, - }); - if (amdSmiOutput.trim()) { - const match = - amdSmiOutput.match(/ROCm version:\s*(\d+\.\d+\.\d+)/i) || - amdSmiOutput.match(/version\s*(\d+\.\d+\.\d+)/i) || - amdSmiOutput.match(/(\d+\.\d+\.\d+)/); - if (match) { - version = match[1]; + if (platform === 'linux' || platform === 'darwin') { + try { + const { stdout: amdSmiOutput } = await execa('amd-smi', { + timeout: 3000, + reject: false, + }); + + if (amdSmiOutput.trim()) { + const match = + amdSmiOutput.match(/ROCm version:\s*(\d+\.\d+\.\d+)/i) || + amdSmiOutput.match(/version\s*(\d+\.\d+\.\d+)/i) || + amdSmiOutput.match(/(\d+\.\d+\.\d+)/); + if (match) { + version = match[1]; + } } - } - } catch {} + } catch {} + } else { + try { + const { stdout: hipccOutput } = await execa('hipcc', ['--version'], { + timeout: 3000, + reject: false, + }); + + if (hipccOutput.trim()) { + const hipVersionMatch = hipccOutput.match( + /HIP version:\s*(\d+\.\d+(?:\.\d+)?)/i + ); + if (hipVersionMatch) { + version = hipVersionMatch[1]; + } + } + } catch {} + } + + let driverVersion: string | undefined; + + if (platform === 'win32') { + try { + const { stdout: driverOutput } = await execa( + 'powershell', + [ + '-Command', + `Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Name -like '*AMD*' -or $_.Name -like '*Radeon*' } | Select-Object -First 1 -ExpandProperty DriverVersion`, + ], + { + timeout: 3000, + reject: false, + } + ); + + if (driverOutput.trim()) { + driverVersion = driverOutput.trim(); + } + } catch {} + } return { supported: devices.length > 0, devices, version, + driverVersion, }; } @@ -422,7 +474,7 @@ export async function detectGPUMemory() { } const result = await safeExecute(async () => { - const gpuData = await getGPUData(); + const gpuData = await getGPUData(true); const memoryInfo: GPUMemoryInfo[] = []; for (const gpu of gpuData) { diff --git a/src/types/hardware.d.ts b/src/types/hardware.d.ts index d869968..da94dc9 100644 --- a/src/types/hardware.d.ts +++ b/src/types/hardware.d.ts @@ -19,11 +19,13 @@ export interface GPUCapabilities { readonly supported: boolean; readonly devices: readonly string[]; readonly version?: string; + readonly driverVersion?: string; }; rocm: { readonly supported: boolean; readonly devices: readonly string[]; readonly version?: string; + readonly driverVersion?: string; }; vulkan: { readonly supported: boolean; diff --git a/src/utils/node/gpu.ts b/src/utils/node/gpu.ts index e3b3dcc..e0b84d6 100644 --- a/src/utils/node/gpu.ts +++ b/src/utils/node/gpu.ts @@ -1,6 +1,7 @@ import { readFile, readdir } from 'fs/promises'; import { join } from 'path'; import { platform } from 'process'; +import { execa } from 'execa'; interface CachedGPUInfo { devicePath: string; @@ -19,9 +20,11 @@ let linuxGpuCache: CachedGPUInfo[] | null = null; let linuxCachePromise: Promise | null = null; -export async function getGPUData() { +export async function getGPUData(forNonMetrics = false) { if (platform === 'linux') { return getLinuxGPUData(); + } else if (platform === 'win32' && forNonMetrics) { + return getWindowsGPUData(); } else { return []; } @@ -143,3 +146,58 @@ async function getLinuxGPUData() { return []; } } + +async function getWindowsGPUData() { + try { + const { stdout } = await execa( + 'powershell', + [ + '-Command', + `$activeGpus = Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Status -eq 'OK' } | Select-Object -ExpandProperty Name; + $gpuKeys = Get-ChildItem 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}' | Where-Object { $_.PSChildName -match '^\\d{4}$' }; + foreach($key in $gpuKeys) { + $props = Get-ItemProperty $key.PSPath -ErrorAction SilentlyContinue; + if($props.DriverDesc -and $props.'HardwareInformation.qwMemorySize' -and $activeGpus -contains $props.DriverDesc) { + $vramBytes = $props.'HardwareInformation.qwMemorySize'; + $vramGB = [math]::Round($vramBytes/1GB, 2); + if($vramGB -gt 0.5) { + Write-Output "$($props.DriverDesc)|$vramGB"; + } + } + }`, + ], + { + timeout: 10000, + reject: false, + } + ); + + if (!stdout.trim()) { + return []; + } + + const gpus: GPUData[] = []; + const lines = stdout.trim().split('\n'); + const seenVram = new Set(); + + for (const line of lines) { + const parts = line.trim().split('|'); + if (parts.length === 2) { + const vramGB = parseFloat(parts[1]); + if (vramGB > 0 && !seenVram.has(vramGB)) { + seenVram.add(vramGB); + gpus.push({ + usage: 0, + memoryUsed: 0, + memoryTotal: vramGB, + temperature: undefined, + }); + } + } + } + + return gpus; + } catch { + return []; + } +} diff --git a/src/utils/systemInfo.ts b/src/utils/systemInfo.ts index cce853b..0a25947 100644 --- a/src/utils/systemInfo.ts +++ b/src/utils/systemInfo.ts @@ -32,9 +32,11 @@ export const createSoftwareItems = (versionInfo: SystemVersionInfo) => [ { label: 'Electron', value: versionInfo.electronVersion }, { label: 'Node.js', - value: versionInfo.nodeJsSystemVersion - ? `${versionInfo.nodeVersion} (System: ${versionInfo.nodeJsSystemVersion})` - : versionInfo.nodeVersion, + value: + versionInfo.nodeJsSystemVersion && + versionInfo.nodeJsSystemVersion !== versionInfo.nodeVersion + ? `${versionInfo.nodeVersion} (system: ${versionInfo.nodeJsSystemVersion})` + : versionInfo.nodeVersion, }, { label: 'Chromium', value: versionInfo.chromeVersion }, { label: 'V8', value: versionInfo.v8Version }, @@ -67,6 +69,13 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => { ? `v${gpuCapabilities.cuda.version}` : 'Available', }); + + if (gpuCapabilities.cuda.driverVersion) { + items.push({ + label: 'NVIDIA Driver', + value: `v${gpuCapabilities.cuda.driverVersion}`, + }); + } } if (gpuCapabilities.rocm.supported) { @@ -76,6 +85,13 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => { ? `v${gpuCapabilities.rocm.version}` : 'Available', }); + + if (gpuCapabilities.rocm.driverVersion) { + items.push({ + label: 'AMD Driver', + value: `v${gpuCapabilities.rocm.driverVersion}`, + }); + } } if (gpuCapabilities.vulkan.supported) {