mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 04:04:44 -07:00
more hardware + driver display improvements
This commit is contained in:
parent
c2287b1d5c
commit
3ef91d1198
6 changed files with 334 additions and 143 deletions
|
|
@ -109,15 +109,15 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
|
||||||
if (gpuMemoryInfo) {
|
if (gpuMemoryInfo) {
|
||||||
const lowVramThreshold = 8;
|
const lowVramThreshold = 8;
|
||||||
const validGpus = gpuMemoryInfo.filter(
|
const validGpus = gpuMemoryInfo.filter(
|
||||||
(gpu) => typeof gpu.totalMemoryGB === 'number'
|
(gpu) => gpu.totalMemoryGB !== null && gpu.totalMemoryGB !== ''
|
||||||
);
|
);
|
||||||
const lowVramGpus = validGpus.filter(
|
const lowVramGpus = validGpus.filter(
|
||||||
(gpu) => gpu.totalMemoryGB! < lowVramThreshold
|
(gpu) => parseFloat(gpu.totalMemoryGB!) < lowVramThreshold
|
||||||
);
|
);
|
||||||
|
|
||||||
if (validGpus.length > 0 && lowVramGpus.length === validGpus.length) {
|
if (validGpus.length > 0 && lowVramGpus.length === validGpus.length) {
|
||||||
const memoryDetails = lowVramGpus
|
const memoryDetails = lowVramGpus
|
||||||
.map((gpu) => `${gpu.totalMemoryGB!.toFixed(1)}GB`)
|
.map((gpu) => `${parseFloat(gpu.totalMemoryGB!).toFixed(1)}GB`)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
warnings.push({
|
warnings.push({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
/* eslint-disable no-comments/disallowComments */
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import si from 'systeminformation';
|
import {
|
||||||
|
cpu as siCpu,
|
||||||
|
cpuFlags,
|
||||||
|
graphics as siGraphics,
|
||||||
|
mem as siMem,
|
||||||
|
memLayout as siMemLayout,
|
||||||
|
} from 'systeminformation';
|
||||||
import { safeExecute } from '@/utils/node/logging';
|
import { safeExecute } from '@/utils/node/logging';
|
||||||
import { getGPUData } from '@/utils/node/gpu';
|
import { getGPUData, isDiscreteBusAddress } from '@/utils/node/gpu';
|
||||||
import type {
|
import type {
|
||||||
CPUCapabilities,
|
CPUCapabilities,
|
||||||
GPUCapabilities,
|
GPUCapabilities,
|
||||||
|
|
@ -12,10 +18,25 @@ import { execa } from 'execa';
|
||||||
import { formatDeviceName } from '@/utils/format';
|
import { formatDeviceName } from '@/utils/format';
|
||||||
import { platform } from 'process';
|
import { platform } from 'process';
|
||||||
|
|
||||||
|
const COMMON_EXEC_OPTIONS = {
|
||||||
|
timeout: 3000,
|
||||||
|
reject: false,
|
||||||
|
};
|
||||||
|
|
||||||
let cpuCapabilitiesCache: CPUCapabilities | null = null;
|
let cpuCapabilitiesCache: CPUCapabilities | null = null;
|
||||||
let basicGPUInfoCache: BasicGPUInfo | null = null;
|
let basicGPUInfoCache: BasicGPUInfo | null = null;
|
||||||
let gpuCapabilitiesCache: GPUCapabilities | null = null;
|
let gpuCapabilitiesCache: GPUCapabilities | null = null;
|
||||||
let gpuMemoryInfoCache: GPUMemoryInfo[] | null = null;
|
let gpuMemoryInfoCache: GPUMemoryInfo[] | null = null;
|
||||||
|
let vulkanInfoCache: {
|
||||||
|
discreteGPUs: {
|
||||||
|
deviceName: string;
|
||||||
|
driverInfo?: string;
|
||||||
|
apiVersion?: string;
|
||||||
|
hasAMD: boolean;
|
||||||
|
hasNVIDIA: boolean;
|
||||||
|
}[];
|
||||||
|
apiVersion?: string;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
export async function detectCPU() {
|
export async function detectCPU() {
|
||||||
if (cpuCapabilitiesCache) {
|
if (cpuCapabilitiesCache) {
|
||||||
|
|
@ -23,7 +44,7 @@ export async function detectCPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await safeExecute(async () => {
|
const result = await safeExecute(async () => {
|
||||||
const [cpu, flags] = await Promise.all([si.cpu(), si.cpuFlags()]);
|
const [cpu, flags] = await Promise.all([siCpu(), cpuFlags()]);
|
||||||
|
|
||||||
const devices: string[] = [];
|
const devices: string[] = [];
|
||||||
if (cpu.brand) {
|
if (cpu.brand) {
|
||||||
|
|
@ -61,14 +82,186 @@ export async function detectGPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await safeExecute(async () => {
|
const result = await safeExecute(async () => {
|
||||||
const graphics = await si.graphics();
|
if (platform === 'linux') {
|
||||||
|
return detectLinuxGPUViaVulkan();
|
||||||
|
} else {
|
||||||
|
return detectGPUViaSI();
|
||||||
|
}
|
||||||
|
}, 'GPU detection failed');
|
||||||
|
|
||||||
|
const fallbackGPUInfo = {
|
||||||
|
hasAMD: false,
|
||||||
|
hasNVIDIA: false,
|
||||||
|
gpuInfo: ['GPU detection failed'],
|
||||||
|
};
|
||||||
|
|
||||||
|
basicGPUInfoCache = result || fallbackGPUInfo;
|
||||||
|
return basicGPUInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
async function getVulkanInfo() {
|
||||||
|
if (vulkanInfoCache) {
|
||||||
|
return vulkanInfoCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { stdout } = await execa(
|
||||||
|
'vulkaninfo',
|
||||||
|
['--summary'],
|
||||||
|
COMMON_EXEC_OPTIONS
|
||||||
|
);
|
||||||
|
|
||||||
|
const discreteGPUs: {
|
||||||
|
deviceName: string;
|
||||||
|
driverInfo?: string;
|
||||||
|
apiVersion?: string;
|
||||||
|
hasAMD: boolean;
|
||||||
|
hasNVIDIA: boolean;
|
||||||
|
}[] = [];
|
||||||
|
let globalApiVersion: string | undefined;
|
||||||
|
|
||||||
|
if (stdout.trim()) {
|
||||||
|
const lines = stdout.split('\n');
|
||||||
|
let foundDiscreteGPU = false;
|
||||||
|
let currentGPU: (typeof discreteGPUs)[0] | null = null;
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (
|
||||||
|
!globalApiVersion &&
|
||||||
|
line.includes('apiVersion') &&
|
||||||
|
line.includes('=')
|
||||||
|
) {
|
||||||
|
const match = line.match(/=\s*(\d+\.\d+(?:\.\d+)?)/);
|
||||||
|
if (match) {
|
||||||
|
globalApiVersion = match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.includes('PHYSICAL_DEVICE_TYPE_DISCRETE_GPU')) {
|
||||||
|
foundDiscreteGPU = true;
|
||||||
|
currentGPU = {
|
||||||
|
deviceName: '',
|
||||||
|
hasAMD: false,
|
||||||
|
hasNVIDIA: false,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
foundDiscreteGPU &&
|
||||||
|
currentGPU &&
|
||||||
|
line.includes('deviceName') &&
|
||||||
|
line.includes('=')
|
||||||
|
) {
|
||||||
|
const parts = line.split('=');
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
const name = parts[1]?.trim();
|
||||||
|
if (name) {
|
||||||
|
currentGPU.deviceName = name;
|
||||||
|
currentGPU.hasAMD =
|
||||||
|
name.toLowerCase().includes('amd') ||
|
||||||
|
name.toLowerCase().includes('radeon');
|
||||||
|
currentGPU.hasNVIDIA =
|
||||||
|
name.toLowerCase().includes('nvidia') ||
|
||||||
|
name.toLowerCase().includes('geforce') ||
|
||||||
|
name.toLowerCase().includes('rtx') ||
|
||||||
|
name.toLowerCase().includes('gtx');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
foundDiscreteGPU &&
|
||||||
|
currentGPU &&
|
||||||
|
line.includes('driverInfo')
|
||||||
|
) {
|
||||||
|
const mesaMatch = line.match(/Mesa\s+(.+)/);
|
||||||
|
if (mesaMatch) {
|
||||||
|
currentGPU.driverInfo = `Mesa ${mesaMatch[1].trim()}`;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
foundDiscreteGPU &&
|
||||||
|
currentGPU &&
|
||||||
|
line.includes('apiVersion') &&
|
||||||
|
line.includes('=')
|
||||||
|
) {
|
||||||
|
const match = line.match(/=\s*(\d+\.\d+(?:\.\d+)?)/);
|
||||||
|
if (match) {
|
||||||
|
currentGPU.apiVersion = match[1];
|
||||||
|
if (!globalApiVersion) {
|
||||||
|
globalApiVersion = match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (foundDiscreteGPU && currentGPU && line.includes('GPU')) {
|
||||||
|
if (currentGPU.deviceName) {
|
||||||
|
discreteGPUs.push(currentGPU);
|
||||||
|
}
|
||||||
|
foundDiscreteGPU = false;
|
||||||
|
currentGPU = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundDiscreteGPU && currentGPU && currentGPU.deviceName) {
|
||||||
|
discreteGPUs.push(currentGPU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vulkanInfoCache = {
|
||||||
|
discreteGPUs,
|
||||||
|
apiVersion: globalApiVersion,
|
||||||
|
};
|
||||||
|
|
||||||
|
return vulkanInfoCache;
|
||||||
|
} catch {
|
||||||
|
vulkanInfoCache = {
|
||||||
|
discreteGPUs: [],
|
||||||
|
};
|
||||||
|
return vulkanInfoCache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detectLinuxGPUViaVulkan() {
|
||||||
|
try {
|
||||||
|
const vulkanInfo = await getVulkanInfo();
|
||||||
|
|
||||||
let hasAMD = false;
|
let hasAMD = false;
|
||||||
let hasNVIDIA = false;
|
let hasNVIDIA = false;
|
||||||
const gpuInfo: string[] = [];
|
const gpuInfo: string[] = [];
|
||||||
|
|
||||||
for (const controller of graphics.controllers) {
|
for (const gpu of vulkanInfo.discreteGPUs) {
|
||||||
// Check vendor for AMD
|
gpuInfo.push(formatDeviceName(gpu.deviceName));
|
||||||
|
|
||||||
|
if (gpu.hasAMD) {
|
||||||
|
hasAMD = true;
|
||||||
|
}
|
||||||
|
if (gpu.hasNVIDIA) {
|
||||||
|
hasNVIDIA = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasAMD,
|
||||||
|
hasNVIDIA,
|
||||||
|
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
hasAMD: false,
|
||||||
|
hasNVIDIA: false,
|
||||||
|
gpuInfo: ['GPU detection failed'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detectGPUViaSI() {
|
||||||
|
const graphics = await siGraphics();
|
||||||
|
|
||||||
|
let hasAMD = false;
|
||||||
|
let hasNVIDIA = false;
|
||||||
|
const gpuInfo: string[] = [];
|
||||||
|
|
||||||
|
const discreteControllers = graphics.controllers.filter(
|
||||||
|
(controller) =>
|
||||||
|
controller.busAddress && isDiscreteBusAddress(controller.busAddress)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const controller of discreteControllers) {
|
||||||
if (
|
if (
|
||||||
controller.vendor?.toLowerCase().includes('amd') ||
|
controller.vendor?.toLowerCase().includes('amd') ||
|
||||||
controller.vendor?.toLowerCase().includes('ati')
|
controller.vendor?.toLowerCase().includes('ati')
|
||||||
|
|
@ -85,23 +278,11 @@ export async function detectGPU() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const basicInfo = {
|
return {
|
||||||
hasAMD,
|
hasAMD,
|
||||||
hasNVIDIA,
|
hasNVIDIA,
|
||||||
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
||||||
};
|
};
|
||||||
basicGPUInfoCache = basicInfo;
|
|
||||||
return basicInfo;
|
|
||||||
}, 'GPU detection failed');
|
|
||||||
|
|
||||||
const fallbackGPUInfo = {
|
|
||||||
hasAMD: false,
|
|
||||||
hasNVIDIA: false,
|
|
||||||
gpuInfo: ['GPU detection failed'],
|
|
||||||
};
|
|
||||||
|
|
||||||
basicGPUInfoCache = result || fallbackGPUInfo;
|
|
||||||
return basicGPUInfoCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function detectGPUCapabilities() {
|
export async function detectGPUCapabilities() {
|
||||||
|
|
@ -125,10 +306,7 @@ export async function detectGPUCapabilities() {
|
||||||
|
|
||||||
async function detectCUDA() {
|
async function detectCUDA() {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa('nvidia-smi', [], {
|
const { stdout } = await execa('nvidia-smi', [], COMMON_EXEC_OPTIONS);
|
||||||
timeout: 5000,
|
|
||||||
reject: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (stdout.trim()) {
|
if (stdout.trim()) {
|
||||||
const errorPatterns = [
|
const errorPatterns = [
|
||||||
|
|
@ -159,11 +337,13 @@ async function detectCUDA() {
|
||||||
const driverMatch = stdout.match(
|
const driverMatch = stdout.match(
|
||||||
/Driver Version:\s*(\d+\.\d+(?:\.\d+)?)/
|
/Driver Version:\s*(\d+\.\d+(?:\.\d+)?)/
|
||||||
);
|
);
|
||||||
|
|
||||||
if (driverMatch) {
|
if (driverMatch) {
|
||||||
driverVersion = driverMatch[1];
|
driverVersion = driverMatch[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
const gpuNameMatch = stdout.match(/\|\s+\d+\s+([^|]+)\s+On\s+\|/g);
|
const gpuNameMatch = stdout.match(/\|\s+\d+\s+([^|]+)\s+On\s+\|/g);
|
||||||
|
|
||||||
if (gpuNameMatch) {
|
if (gpuNameMatch) {
|
||||||
for (const match of gpuNameMatch) {
|
for (const match of gpuNameMatch) {
|
||||||
const name = match
|
const name = match
|
||||||
|
|
@ -193,10 +373,7 @@ async function detectCUDA() {
|
||||||
export async function detectROCm() {
|
export async function detectROCm() {
|
||||||
try {
|
try {
|
||||||
const rocminfoCommand = platform === 'win32' ? 'hipInfo' : 'rocminfo';
|
const rocminfoCommand = platform === 'win32' ? 'hipInfo' : 'rocminfo';
|
||||||
const { stdout } = await execa(rocminfoCommand, [], {
|
const { stdout } = await execa(rocminfoCommand, [], COMMON_EXEC_OPTIONS);
|
||||||
timeout: 5000,
|
|
||||||
reject: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (stdout.trim()) {
|
if (stdout.trim()) {
|
||||||
const devices: string[] = [];
|
const devices: string[] = [];
|
||||||
|
|
@ -258,10 +435,10 @@ export async function detectROCm() {
|
||||||
|
|
||||||
if (platform === 'linux' || platform === 'darwin') {
|
if (platform === 'linux' || platform === 'darwin') {
|
||||||
try {
|
try {
|
||||||
const { stdout: amdSmiOutput } = await execa('amd-smi', {
|
const { stdout: amdSmiOutput } = await execa(
|
||||||
timeout: 3000,
|
'amd-smi',
|
||||||
reject: false,
|
COMMON_EXEC_OPTIONS
|
||||||
});
|
);
|
||||||
|
|
||||||
if (amdSmiOutput.trim()) {
|
if (amdSmiOutput.trim()) {
|
||||||
const match =
|
const match =
|
||||||
|
|
@ -275,10 +452,11 @@ export async function detectROCm() {
|
||||||
} catch {}
|
} catch {}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const { stdout: hipccOutput } = await execa('hipcc', ['--version'], {
|
const { stdout: hipccOutput } = await execa(
|
||||||
timeout: 3000,
|
'hipcc',
|
||||||
reject: false,
|
['--version'],
|
||||||
});
|
COMMON_EXEC_OPTIONS
|
||||||
|
);
|
||||||
|
|
||||||
if (hipccOutput.trim()) {
|
if (hipccOutput.trim()) {
|
||||||
const hipVersionMatch = hipccOutput.match(
|
const hipVersionMatch = hipccOutput.match(
|
||||||
|
|
@ -301,16 +479,24 @@ export async function detectROCm() {
|
||||||
'-Command',
|
'-Command',
|
||||||
`Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Name -like '*AMD*' -or $_.Name -like '*Radeon*' } | Select-Object -First 1 -ExpandProperty DriverVersion`,
|
`Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Name -like '*AMD*' -or $_.Name -like '*Radeon*' } | Select-Object -First 1 -ExpandProperty DriverVersion`,
|
||||||
],
|
],
|
||||||
{
|
COMMON_EXEC_OPTIONS
|
||||||
timeout: 3000,
|
|
||||||
reject: false,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (driverOutput.trim()) {
|
if (driverOutput.trim()) {
|
||||||
driverVersion = driverOutput.trim();
|
driverVersion = driverOutput.trim();
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
} else if (platform === 'linux') {
|
||||||
|
try {
|
||||||
|
const vulkanInfo = await getVulkanInfo();
|
||||||
|
|
||||||
|
for (const gpu of vulkanInfo.discreteGPUs) {
|
||||||
|
if (gpu.driverInfo) {
|
||||||
|
driverVersion = gpu.driverInfo;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -329,48 +515,19 @@ export async function detectROCm() {
|
||||||
|
|
||||||
async function detectVulkan() {
|
async function detectVulkan() {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa('vulkaninfo', ['--summary'], {
|
const vulkanInfo = await getVulkanInfo();
|
||||||
timeout: 5000,
|
|
||||||
reject: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (stdout.trim()) {
|
|
||||||
const devices: string[] = [];
|
const devices: string[] = [];
|
||||||
const lines = stdout.split('\n');
|
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const gpu of vulkanInfo.discreteGPUs) {
|
||||||
if (line.includes('deviceName') && line.includes('=')) {
|
devices.push(formatDeviceName(gpu.deviceName));
|
||||||
const parts = line.split('=');
|
|
||||||
if (parts.length >= 2) {
|
|
||||||
const name = parts[1]?.trim();
|
|
||||||
if (name) {
|
|
||||||
devices.push(formatDeviceName(name));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = 'Unknown';
|
|
||||||
try {
|
|
||||||
const apiVersionLine = lines.find(
|
|
||||||
(line) => line.includes('apiVersion') && line.includes('=')
|
|
||||||
);
|
|
||||||
if (apiVersionLine) {
|
|
||||||
const match = apiVersionLine.match(/=\s*(\d+\.\d+(?:\.\d+)?)/);
|
|
||||||
if (match) {
|
|
||||||
version = match[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
supported: devices.length > 0,
|
supported: devices.length > 0,
|
||||||
devices,
|
devices,
|
||||||
version,
|
version: vulkanInfo.apiVersion || 'Unknown',
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
return { supported: false, devices: [], version: 'Unknown' };
|
|
||||||
} catch {
|
} catch {
|
||||||
return { supported: false, devices: [], version: 'Unknown' };
|
return { supported: false, devices: [], version: 'Unknown' };
|
||||||
}
|
}
|
||||||
|
|
@ -430,10 +587,7 @@ function findDeviceNameInClInfo(lines: string[], startIndex: number) {
|
||||||
|
|
||||||
async function detectCLBlast() {
|
async function detectCLBlast() {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa('clinfo', [], {
|
const { stdout } = await execa('clinfo', [], COMMON_EXEC_OPTIONS);
|
||||||
timeout: 3000,
|
|
||||||
reject: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (stdout.trim()) {
|
if (stdout.trim()) {
|
||||||
const devices = parseClInfoOutput(stdout);
|
const devices = parseClInfoOutput(stdout);
|
||||||
|
|
@ -485,7 +639,7 @@ export async function detectGPUMemory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryInfo.push({
|
memoryInfo.push({
|
||||||
totalMemoryGB: vram,
|
totalMemoryGB: vram?.toFixed(2) || null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,9 +652,9 @@ export async function detectGPUMemory() {
|
||||||
|
|
||||||
export const detectSystemMemory = async () => {
|
export const detectSystemMemory = async () => {
|
||||||
try {
|
try {
|
||||||
const [memInfo, memLayout] = await Promise.all([si.mem(), si.memLayout()]);
|
const [memInfo, memLayout] = await Promise.all([siMem(), siMemLayout()]);
|
||||||
|
|
||||||
const totalGB = Math.round(memInfo.total / 1024 ** 3);
|
const totalGB = (memInfo.total / 1024 ** 3).toFixed(2);
|
||||||
|
|
||||||
let speed: number | undefined;
|
let speed: number | undefined;
|
||||||
let type: string | undefined;
|
let type: string | undefined;
|
||||||
|
|
@ -532,7 +686,7 @@ export const detectSystemMemory = async () => {
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {
|
return {
|
||||||
totalGB: Math.round((await si.mem()).total / 1024 ** 3),
|
totalGB: ((await siMem()).total / 1024 ** 3).toFixed(2),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import si from 'systeminformation';
|
import { mem, cpuTemperature, currentLoad } from 'systeminformation';
|
||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
import { platform } from 'process';
|
import { platform } from 'process';
|
||||||
import { spawn } from 'child_process';
|
import { spawn } from 'child_process';
|
||||||
|
|
@ -95,14 +95,14 @@ export function stopMonitoring() {
|
||||||
|
|
||||||
async function collectAndSendCpuMetrics() {
|
async function collectAndSendCpuMetrics() {
|
||||||
await tryExecute(async () => {
|
await tryExecute(async () => {
|
||||||
const cpuData = await si.currentLoad();
|
const cpuData = await currentLoad();
|
||||||
const metrics: CpuMetrics = {
|
const metrics: CpuMetrics = {
|
||||||
usage: Math.round(cpuData.currentLoad),
|
usage: Math.round(cpuData.currentLoad),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (platform === 'linux') {
|
if (platform === 'linux') {
|
||||||
try {
|
try {
|
||||||
const tempData = await si.cpuTemperature();
|
const tempData = await cpuTemperature();
|
||||||
metrics.temperature =
|
metrics.temperature =
|
||||||
tempData.main && tempData.main > 0
|
tempData.main && tempData.main > 0
|
||||||
? Math.round(tempData.main)
|
? Math.round(tempData.main)
|
||||||
|
|
@ -118,7 +118,7 @@ async function collectAndSendCpuMetrics() {
|
||||||
|
|
||||||
async function collectAndSendMemoryMetrics() {
|
async function collectAndSendMemoryMetrics() {
|
||||||
await tryExecute(async () => {
|
await tryExecute(async () => {
|
||||||
const memData = await si.mem();
|
const memData = await mem();
|
||||||
const usedBytes = memData.active || memData.used;
|
const usedBytes = memData.active || memData.used;
|
||||||
const totalBytes = memData.total;
|
const totalBytes = memData.total;
|
||||||
const metrics: MemoryMetrics = {
|
const metrics: MemoryMetrics = {
|
||||||
|
|
|
||||||
4
src/types/hardware.d.ts
vendored
4
src/types/hardware.d.ts
vendored
|
|
@ -5,11 +5,11 @@ export interface CPUCapabilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GPUMemoryInfo {
|
export interface GPUMemoryInfo {
|
||||||
totalMemoryGB: number | null;
|
totalMemoryGB: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemMemoryInfo {
|
export interface SystemMemoryInfo {
|
||||||
totalGB: number;
|
totalGB: string;
|
||||||
speed?: number;
|
speed?: number;
|
||||||
type?: string;
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,23 @@ async function initializeLinuxGPUCache() {
|
||||||
(parseInt(memTotalData.trim(), 10) || 0) / (1024 * 1024 * 1024)
|
(parseInt(memTotalData.trim(), 10) || 0) / (1024 * 1024 * 1024)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (memoryTotal > 0) {
|
if (memoryTotal >= 1) {
|
||||||
|
let isDiscrete = false;
|
||||||
|
try {
|
||||||
|
const busAddress = await readFile(`${devicePath}/uevent`, 'utf8');
|
||||||
|
const pciMatch = busAddress.match(
|
||||||
|
/PCI_SLOT_NAME=([0-9a-f]{4}:[0-9a-f]{2}:[0-9a-f]{2}\.[0-9a-f])/i
|
||||||
|
);
|
||||||
|
|
||||||
|
if (pciMatch) {
|
||||||
|
const fullAddress = pciMatch[1];
|
||||||
|
const busAddress = fullAddress.substring(5);
|
||||||
|
|
||||||
|
isDiscrete = isDiscreteBusAddress(busAddress);
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
if (isDiscrete) {
|
||||||
let hwmonPath: string | undefined;
|
let hwmonPath: string | undefined;
|
||||||
try {
|
try {
|
||||||
const hwmonEntries = await readdir(`${devicePath}/hwmon`);
|
const hwmonEntries = await readdir(`${devicePath}/hwmon`);
|
||||||
|
|
@ -78,6 +94,7 @@ async function initializeLinuxGPUCache() {
|
||||||
hwmonPath,
|
hwmonPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -153,14 +170,14 @@ async function getWindowsGPUData() {
|
||||||
'powershell',
|
'powershell',
|
||||||
[
|
[
|
||||||
'-Command',
|
'-Command',
|
||||||
`$activeGpus = Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Status -eq 'OK' } | Select-Object -ExpandProperty Name;
|
`$activeGpus = Get-CimInstance -ClassName Win32_VideoController | Where-Object { $_.Status -eq 'OK' -and $_.Name -notlike '*Intel*UHD*' -and $_.Name -notlike '*Intel*Iris*' -and $_.Name -notlike '*Basic Display*' } | Select-Object -ExpandProperty Name;
|
||||||
$gpuKeys = Get-ChildItem 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}' | Where-Object { $_.PSChildName -match '^\\d{4}$' };
|
$gpuKeys = Get-ChildItem 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}' | Where-Object { $_.PSChildName -match '^\\d{4}$' };
|
||||||
foreach($key in $gpuKeys) {
|
foreach($key in $gpuKeys) {
|
||||||
$props = Get-ItemProperty $key.PSPath -ErrorAction SilentlyContinue;
|
$props = Get-ItemProperty $key.PSPath -ErrorAction SilentlyContinue;
|
||||||
if($props.DriverDesc -and $props.'HardwareInformation.qwMemorySize' -and $activeGpus -contains $props.DriverDesc) {
|
if($props.DriverDesc -and $props.'HardwareInformation.qwMemorySize' -and $activeGpus -contains $props.DriverDesc) {
|
||||||
$vramBytes = $props.'HardwareInformation.qwMemorySize';
|
$vramBytes = $props.'HardwareInformation.qwMemorySize';
|
||||||
$vramGB = [math]::Round($vramBytes/1GB, 2);
|
$vramGB = [math]::Round($vramBytes/1GB, 2);
|
||||||
if($vramGB -gt 0.5) {
|
if($vramGB -ge 1) {
|
||||||
Write-Output "$($props.DriverDesc)|$vramGB";
|
Write-Output "$($props.DriverDesc)|$vramGB";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,3 +218,26 @@ async function getWindowsGPUData() {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDiscreteBusAddress(busAddress?: string) {
|
||||||
|
if (!busAddress) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[0-9a-f]{2}:\d{2}\.\d$/i.test(busAddress)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const busNumber = parseInt(busAddress.substring(0, 2), 16);
|
||||||
|
const deviceNumber = parseInt(busAddress.substring(3, 5), 16);
|
||||||
|
|
||||||
|
if (busNumber === 0 && deviceNumber === 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busNumber > 0 && busNumber <= 15) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,14 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'CUDA',
|
label: 'CUDA',
|
||||||
value: gpuCapabilities.cuda.version
|
value: gpuCapabilities.cuda.version
|
||||||
? `v${gpuCapabilities.cuda.version}`
|
? gpuCapabilities.cuda.version
|
||||||
: 'Available',
|
: 'Available',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (gpuCapabilities.cuda.driverVersion) {
|
if (gpuCapabilities.cuda.driverVersion) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'NVIDIA Driver',
|
label: 'NVIDIA Driver',
|
||||||
value: `v${gpuCapabilities.cuda.driverVersion}`,
|
value: gpuCapabilities.cuda.driverVersion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,14 +82,14 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'ROCm',
|
label: 'ROCm',
|
||||||
value: gpuCapabilities.rocm.version
|
value: gpuCapabilities.rocm.version
|
||||||
? `v${gpuCapabilities.rocm.version}`
|
? gpuCapabilities.rocm.version
|
||||||
: 'Available',
|
: 'Available',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (gpuCapabilities.rocm.driverVersion) {
|
if (gpuCapabilities.rocm.driverVersion) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'AMD Driver',
|
label: 'AMD Driver',
|
||||||
value: `v${gpuCapabilities.rocm.driverVersion}`,
|
value: gpuCapabilities.rocm.driverVersion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Vulkan',
|
label: 'Vulkan',
|
||||||
value: gpuCapabilities.vulkan.version
|
value: gpuCapabilities.vulkan.version
|
||||||
? `v${gpuCapabilities.vulkan.version}`
|
? gpuCapabilities.vulkan.version
|
||||||
: 'Available',
|
: 'Available',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -112,7 +112,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'CLBlast',
|
label: 'CLBlast',
|
||||||
value: gpuCapabilities.clblast.version
|
value: gpuCapabilities.clblast.version
|
||||||
? `v${gpuCapabilities.clblast.version}`
|
? gpuCapabilities.clblast.version
|
||||||
: 'Available',
|
: 'Available',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -135,9 +135,8 @@ export const createHardwareItems = (hardwareInfo: HardwareInfo) => [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'RAM',
|
label: 'RAM',
|
||||||
value:
|
value: hardwareInfo.systemMemory
|
||||||
hardwareInfo.systemMemory && hardwareInfo.systemMemory.totalGB > 0
|
? `${hardwareInfo.systemMemory.totalGB} GB${
|
||||||
? `${hardwareInfo.systemMemory.totalGB.toFixed(1)} GB${
|
|
||||||
hardwareInfo.systemMemory.type
|
hardwareInfo.systemMemory.type
|
||||||
? ` ${hardwareInfo.systemMemory.type}`
|
? ` ${hardwareInfo.systemMemory.type}`
|
||||||
: ''
|
: ''
|
||||||
|
|
@ -157,9 +156,7 @@ export const createHardwareItems = (hardwareInfo: HardwareInfo) => [
|
||||||
...(hardwareInfo.gpuMemory && hardwareInfo.gpuMemory.length > 0
|
...(hardwareInfo.gpuMemory && hardwareInfo.gpuMemory.length > 0
|
||||||
? hardwareInfo.gpuMemory.map((mem, index) => ({
|
? hardwareInfo.gpuMemory.map((mem, index) => ({
|
||||||
label: `VRAM ${index > 0 ? index + 1 : ''}`.trim(),
|
label: `VRAM ${index > 0 ? index + 1 : ''}`.trim(),
|
||||||
value: mem.totalMemoryGB
|
value: mem.totalMemoryGB ? `${mem.totalMemoryGB} GB` : 'Unknown',
|
||||||
? `${mem.totalMemoryGB.toFixed(1)} GB`
|
|
||||||
: 'Unknown',
|
|
||||||
}))
|
}))
|
||||||
: []),
|
: []),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue