windows vram display in about, better rocm display on windows, amd/nvidia driver display

This commit is contained in:
lone-cloud 2025-09-26 00:28:42 -07:00
parent ee710c4ac1
commit d628896699
5 changed files with 173 additions and 44 deletions

View file

@ -108,13 +108,14 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
if (gpuMemoryInfo) { if (gpuMemoryInfo) {
const lowVramThreshold = 8; const lowVramThreshold = 8;
const lowVramGpus = gpuMemoryInfo.filter( const validGpus = gpuMemoryInfo.filter(
(gpu) => (gpu) => typeof gpu.totalMemoryGB === 'number'
typeof gpu.totalMemoryGB === 'number' && );
gpu.totalMemoryGB < lowVramThreshold const lowVramGpus = validGpus.filter(
(gpu) => gpu.totalMemoryGB! < lowVramThreshold
); );
if (lowVramGpus.length > 0) { if (validGpus.length > 0 && lowVramGpus.length === validGpus.length) {
const memoryDetails = lowVramGpus const memoryDetails = lowVramGpus
.map((gpu) => `${gpu.totalMemoryGB!.toFixed(1)}GB`) .map((gpu) => `${gpu.totalMemoryGB!.toFixed(1)}GB`)
.join(', '); .join(', ');

View file

@ -28,7 +28,7 @@ export async function detectCPU() {
const devices: string[] = []; const devices: string[] = [];
if (cpu.brand) { if (cpu.brand) {
devices.push( 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() { async function detectCUDA() {
try { try {
const { stdout } = await execa( const { stdout } = await execa('nvidia-smi', [], {
'nvidia-smi', timeout: 5000,
['--query-gpu=name,driver_version', '--format=csv,noheader,nounits'], reject: false,
{ });
timeout: 5000,
reject: false,
}
);
if (stdout.trim()) { if (stdout.trim()) {
// Check for error messages that indicate nvidia-smi failed
const errorPatterns = [ const errorPatterns = [
'NVIDIA-SMI has failed', 'NVIDIA-SMI has failed',
'No devices found', 'No devices found',
@ -152,24 +147,39 @@ async function detectCUDA() {
return { supported: false, devices: [] } as const; return { supported: false, devices: [] } as const;
} }
const lines = stdout.trim().split('\n');
const devices: string[] = []; const devices: string[] = [];
let version: string | undefined; let cudaVersion: string | undefined;
let driverVersion: string | undefined;
for (const line of lines) { const cudaMatch = stdout.match(/CUDA Version:\s*(\d+\.\d+)/);
const parts = line.split(','); if (cudaMatch) {
const rawName = parts[0]?.trim() || 'Unknown NVIDIA GPU'; cudaVersion = cudaMatch[1];
devices.push(formatDeviceName(rawName)); }
if (!version && parts[1]?.trim()) { const driverMatch = stdout.match(
version = parts[1].trim(); /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 { return {
supported: devices.length > 0, supported: devices.length > 0 || !!cudaVersion,
devices, devices,
version, version: cudaVersion,
driverVersion,
} as const; } as const;
} }
@ -245,27 +255,69 @@ export async function detectROCm() {
} }
let version: string | undefined; let version: string | undefined;
try {
const { stdout: amdSmiOutput } = await execa('amd-smi', {
timeout: 3000,
reject: false,
});
if (amdSmiOutput.trim()) { if (platform === 'linux' || platform === 'darwin') {
const match = try {
amdSmiOutput.match(/ROCm version:\s*(\d+\.\d+\.\d+)/i) || const { stdout: amdSmiOutput } = await execa('amd-smi', {
amdSmiOutput.match(/version\s*(\d+\.\d+\.\d+)/i) || timeout: 3000,
amdSmiOutput.match(/(\d+\.\d+\.\d+)/); reject: false,
if (match) { });
version = match[1];
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 { return {
supported: devices.length > 0, supported: devices.length > 0,
devices, devices,
version, version,
driverVersion,
}; };
} }
@ -422,7 +474,7 @@ export async function detectGPUMemory() {
} }
const result = await safeExecute(async () => { const result = await safeExecute(async () => {
const gpuData = await getGPUData(); const gpuData = await getGPUData(true);
const memoryInfo: GPUMemoryInfo[] = []; const memoryInfo: GPUMemoryInfo[] = [];
for (const gpu of gpuData) { for (const gpu of gpuData) {

View file

@ -19,11 +19,13 @@ export interface GPUCapabilities {
readonly supported: boolean; readonly supported: boolean;
readonly devices: readonly string[]; readonly devices: readonly string[];
readonly version?: string; readonly version?: string;
readonly driverVersion?: string;
}; };
rocm: { rocm: {
readonly supported: boolean; readonly supported: boolean;
readonly devices: readonly string[]; readonly devices: readonly string[];
readonly version?: string; readonly version?: string;
readonly driverVersion?: string;
}; };
vulkan: { vulkan: {
readonly supported: boolean; readonly supported: boolean;

View file

@ -1,6 +1,7 @@
import { readFile, readdir } from 'fs/promises'; import { readFile, readdir } from 'fs/promises';
import { join } from 'path'; import { join } from 'path';
import { platform } from 'process'; import { platform } from 'process';
import { execa } from 'execa';
interface CachedGPUInfo { interface CachedGPUInfo {
devicePath: string; devicePath: string;
@ -19,9 +20,11 @@ let linuxGpuCache: CachedGPUInfo[] | null = null;
let linuxCachePromise: Promise<CachedGPUInfo[]> | null = null; let linuxCachePromise: Promise<CachedGPUInfo[]> | null = null;
export async function getGPUData() { export async function getGPUData(forNonMetrics = false) {
if (platform === 'linux') { if (platform === 'linux') {
return getLinuxGPUData(); return getLinuxGPUData();
} else if (platform === 'win32' && forNonMetrics) {
return getWindowsGPUData();
} else { } else {
return []; return [];
} }
@ -143,3 +146,58 @@ async function getLinuxGPUData() {
return []; 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<number>();
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 [];
}
}

View file

@ -32,9 +32,11 @@ export const createSoftwareItems = (versionInfo: SystemVersionInfo) => [
{ label: 'Electron', value: versionInfo.electronVersion }, { label: 'Electron', value: versionInfo.electronVersion },
{ {
label: 'Node.js', label: 'Node.js',
value: versionInfo.nodeJsSystemVersion value:
? `${versionInfo.nodeVersion} (System: ${versionInfo.nodeJsSystemVersion})` versionInfo.nodeJsSystemVersion &&
: versionInfo.nodeVersion, versionInfo.nodeJsSystemVersion !== versionInfo.nodeVersion
? `${versionInfo.nodeVersion} (system: ${versionInfo.nodeJsSystemVersion})`
: versionInfo.nodeVersion,
}, },
{ label: 'Chromium', value: versionInfo.chromeVersion }, { label: 'Chromium', value: versionInfo.chromeVersion },
{ label: 'V8', value: versionInfo.v8Version }, { label: 'V8', value: versionInfo.v8Version },
@ -67,6 +69,13 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
? `v${gpuCapabilities.cuda.version}` ? `v${gpuCapabilities.cuda.version}`
: 'Available', : 'Available',
}); });
if (gpuCapabilities.cuda.driverVersion) {
items.push({
label: 'NVIDIA Driver',
value: `v${gpuCapabilities.cuda.driverVersion}`,
});
}
} }
if (gpuCapabilities.rocm.supported) { if (gpuCapabilities.rocm.supported) {
@ -76,6 +85,13 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
? `v${gpuCapabilities.rocm.version}` ? `v${gpuCapabilities.rocm.version}`
: 'Available', : 'Available',
}); });
if (gpuCapabilities.rocm.driverVersion) {
items.push({
label: 'AMD Driver',
value: `v${gpuCapabilities.rocm.driverVersion}`,
});
}
} }
if (gpuCapabilities.vulkan.supported) { if (gpuCapabilities.vulkan.supported) {