mirror of
https://github.com/lone-cloud/gerbil
synced 2026-06-04 04:04:44 -07:00
windows hardware detection improvements, hide integrated gpus for the clblast backend, dont try to detect avx/avx2 flags in the CPU as it's unreliable, use --usecpu flag when CPU backend is selected
This commit is contained in:
parent
3652ce4686
commit
48338b9904
11 changed files with 219 additions and 183 deletions
|
|
@ -7,9 +7,19 @@ export const BackendSelectItem = ({
|
||||||
label,
|
label,
|
||||||
devices,
|
devices,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: BackendSelectItemProps) => (
|
}: BackendSelectItemProps) => {
|
||||||
|
const renderDeviceName = (
|
||||||
|
device: string | { name: string; isIntegrated: boolean }
|
||||||
|
) => {
|
||||||
|
const deviceName = typeof device === 'string' ? device : device.name;
|
||||||
|
return deviceName.length > 25
|
||||||
|
? `${deviceName.slice(0, 25)}...`
|
||||||
|
: deviceName;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<Group justify="space-between" wrap="nowrap">
|
<Group justify="space-between" wrap="nowrap">
|
||||||
<Box w={!disabled ? '3rem' : 'auto'}>
|
<Box w={!disabled ? '3.5rem' : 'auto'}>
|
||||||
<Text size="sm" truncate>
|
<Text size="sm" truncate>
|
||||||
{label}
|
{label}
|
||||||
{disabled && (
|
{disabled && (
|
||||||
|
|
@ -19,19 +29,29 @@ export const BackendSelectItem = ({
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
{devices && devices.length > 0 && (
|
{devices &&
|
||||||
|
devices.length > 0 &&
|
||||||
|
(() => {
|
||||||
|
const discreteDevices = devices.filter(
|
||||||
|
(device) => typeof device === 'string' || !device.isIntegrated
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
discreteDevices.length > 0 && (
|
||||||
<Group gap={4}>
|
<Group gap={4}>
|
||||||
{devices.slice(0, 2).map((device, index) => (
|
{discreteDevices.slice(0, 2).map((device, index) => (
|
||||||
<Badge key={index} size="md" variant="light" color="blue">
|
<Badge key={index} size="md" variant="light" color="blue">
|
||||||
{device.length > 25 ? `${device.slice(0, 25)}...` : device}
|
{renderDeviceName(device)}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
{devices.length > 2 && (
|
{discreteDevices.length > 2 && (
|
||||||
<Badge size="md" variant="light" color="gray">
|
<Badge size="md" variant="light" color="gray">
|
||||||
+{devices.length - 2}
|
+{discreteDevices.length - 2}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,18 @@ export const GpuDeviceSelector = ({
|
||||||
backend === 'rocm' ||
|
backend === 'rocm' ||
|
||||||
backend === 'vulkan' ||
|
backend === 'vulkan' ||
|
||||||
backend === 'clblast';
|
backend === 'clblast';
|
||||||
const hasMultipleDevices =
|
|
||||||
selectedBackend?.devices && selectedBackend.devices.length > 1;
|
const getDiscreteDeviceCount = () => {
|
||||||
|
if (!selectedBackend?.devices) return 0;
|
||||||
|
if (backend === 'clblast') {
|
||||||
|
return selectedBackend.devices.filter(
|
||||||
|
(device) => typeof device === 'string' || !device.isIntegrated
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
return selectedBackend.devices.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasMultipleDevices = getDiscreteDeviceCount() > 1;
|
||||||
const showTensorSplit =
|
const showTensorSplit =
|
||||||
(backend === 'cuda' || backend === 'rocm' || backend === 'vulkan') &&
|
(backend === 'cuda' || backend === 'rocm' || backend === 'vulkan') &&
|
||||||
hasMultipleDevices &&
|
hasMultipleDevices &&
|
||||||
|
|
@ -36,19 +46,42 @@ export const GpuDeviceSelector = ({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceOptions =
|
const deviceOptions = (() => {
|
||||||
backend === 'clblast'
|
if (!selectedBackend?.devices) return [];
|
||||||
? selectedBackend.devices!.map((device, index) => ({
|
|
||||||
|
if (backend === 'clblast') {
|
||||||
|
return selectedBackend.devices
|
||||||
|
.map((device, index) => {
|
||||||
|
if (typeof device === 'object' && device.isIntegrated) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const deviceName = typeof device === 'string' ? device : device.name;
|
||||||
|
return {
|
||||||
value: index.toString(),
|
value: index.toString(),
|
||||||
label: `GPU ${index}: ${device}`,
|
label: `GPU ${index}: ${deviceName}`,
|
||||||
}))
|
};
|
||||||
: [
|
})
|
||||||
|
.filter(
|
||||||
|
(option): option is NonNullable<typeof option> => option !== null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
{ value: 'all', label: 'All GPUs' },
|
{ value: 'all', label: 'All GPUs' },
|
||||||
...selectedBackend.devices!.map((device, index) => ({
|
...selectedBackend.devices.map((device, index) => {
|
||||||
|
const deviceName =
|
||||||
|
typeof device === 'string'
|
||||||
|
? device
|
||||||
|
: typeof device === 'object' && 'name' in device
|
||||||
|
? device.name
|
||||||
|
: String(device);
|
||||||
|
return {
|
||||||
value: index.toString(),
|
value: index.toString(),
|
||||||
label: `GPU ${index}: ${device}`,
|
label: `GPU ${index}: ${deviceName}`,
|
||||||
})),
|
};
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,6 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
|
||||||
model,
|
model,
|
||||||
sdmodel,
|
sdmodel,
|
||||||
backend,
|
backend,
|
||||||
noavx2,
|
|
||||||
failsafe,
|
|
||||||
configLoaded,
|
configLoaded,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,11 +85,15 @@ const buildModelArgs = (
|
||||||
const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => {
|
const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => {
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
|
|
||||||
|
const isGpuBackend = launchArgs.backend && launchArgs.backend !== 'cpu';
|
||||||
|
|
||||||
|
if (isGpuBackend) {
|
||||||
if (launchArgs.autoGpuLayers) {
|
if (launchArgs.autoGpuLayers) {
|
||||||
args.push('--gpulayers', '-1');
|
args.push('--gpulayers', '-1');
|
||||||
} else if (launchArgs.gpuLayers > 0) {
|
} else if (launchArgs.gpuLayers > 0) {
|
||||||
args.push('--gpulayers', launchArgs.gpuLayers.toString());
|
args.push('--gpulayers', launchArgs.gpuLayers.toString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (launchArgs.contextSize) {
|
if (launchArgs.contextSize) {
|
||||||
args.push('--contextsize', launchArgs.contextSize.toString());
|
args.push('--contextsize', launchArgs.contextSize.toString());
|
||||||
|
|
@ -193,6 +197,9 @@ const buildBackendArgs = (launchArgs: LaunchArgs) => {
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
|
|
||||||
if (!launchArgs.backend || launchArgs.backend === 'cpu') {
|
if (!launchArgs.backend || launchArgs.backend === 'cpu') {
|
||||||
|
if (launchArgs.backend === 'cpu') {
|
||||||
|
args.push('--usecpu');
|
||||||
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ interface UseWarningsProps {
|
||||||
model: string;
|
model: string;
|
||||||
sdmodel: string;
|
sdmodel: string;
|
||||||
backend?: string;
|
backend?: string;
|
||||||
noavx2?: boolean;
|
|
||||||
failsafe?: boolean;
|
|
||||||
configLoaded?: boolean;
|
configLoaded?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,9 +131,6 @@ const checkVramWarnings = async (backend: string): Promise<Warning[]> => {
|
||||||
|
|
||||||
const checkCpuWarnings = (
|
const checkCpuWarnings = (
|
||||||
backend: string,
|
backend: string,
|
||||||
cpuCapabilities: { avx: boolean; avx2: boolean },
|
|
||||||
noavx2: boolean,
|
|
||||||
failsafe: boolean,
|
|
||||||
availableBackends: BackendOption[]
|
availableBackends: BackendOption[]
|
||||||
) => {
|
) => {
|
||||||
const warnings: Warning[] = [];
|
const warnings: Warning[] = [];
|
||||||
|
|
@ -144,22 +139,6 @@ const checkCpuWarnings = (
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cpuCapabilities.avx2 && !noavx2) {
|
|
||||||
warnings.push({
|
|
||||||
type: 'warning',
|
|
||||||
message:
|
|
||||||
'Your CPU does not support AVX2. Enable the "Disable AVX2" option on the Advanced tab to avoid crashes.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cpuCapabilities.avx && !cpuCapabilities.avx2 && !failsafe) {
|
|
||||||
warnings.push({
|
|
||||||
type: 'warning',
|
|
||||||
message:
|
|
||||||
'Your CPU does not support AVX or AVX2. Enable the "Failsafe" option on the Advanced tab to avoid crashes.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
availableBackends.length > 0 &&
|
availableBackends.length > 0 &&
|
||||||
availableBackends.some((b) => b.value === 'cpu')
|
availableBackends.some((b) => b.value === 'cpu')
|
||||||
|
|
@ -177,11 +156,8 @@ const checkCpuWarnings = (
|
||||||
const checkBackendWarnings = async (params?: {
|
const checkBackendWarnings = async (params?: {
|
||||||
backend: string;
|
backend: string;
|
||||||
cpuCapabilities: {
|
cpuCapabilities: {
|
||||||
avx: boolean;
|
devices: string[];
|
||||||
avx2: boolean;
|
|
||||||
} | null;
|
} | null;
|
||||||
noavx2: boolean;
|
|
||||||
failsafe: boolean;
|
|
||||||
availableBackends: BackendOption[];
|
availableBackends: BackendOption[];
|
||||||
}): Promise<Warning[]> => {
|
}): Promise<Warning[]> => {
|
||||||
const warnings: Warning[] = [];
|
const warnings: Warning[] = [];
|
||||||
|
|
@ -204,20 +180,13 @@ const checkBackendWarnings = async (params?: {
|
||||||
warnings.push(...gpuWarnings);
|
warnings.push(...gpuWarnings);
|
||||||
|
|
||||||
if (params) {
|
if (params) {
|
||||||
const { backend, cpuCapabilities, noavx2, failsafe, availableBackends } =
|
const { backend, cpuCapabilities, availableBackends } = params;
|
||||||
params;
|
|
||||||
|
|
||||||
const vramWarnings = await checkVramWarnings(backend);
|
const vramWarnings = await checkVramWarnings(backend);
|
||||||
warnings.push(...vramWarnings);
|
warnings.push(...vramWarnings);
|
||||||
|
|
||||||
if (cpuCapabilities) {
|
if (cpuCapabilities) {
|
||||||
const cpuWarnings = checkCpuWarnings(
|
const cpuWarnings = checkCpuWarnings(backend, availableBackends);
|
||||||
backend,
|
|
||||||
cpuCapabilities,
|
|
||||||
noavx2,
|
|
||||||
failsafe,
|
|
||||||
availableBackends
|
|
||||||
);
|
|
||||||
warnings.push(...cpuWarnings);
|
warnings.push(...cpuWarnings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,8 +198,6 @@ export const useWarnings = ({
|
||||||
model,
|
model,
|
||||||
sdmodel,
|
sdmodel,
|
||||||
backend,
|
backend,
|
||||||
noavx2 = false,
|
|
||||||
failsafe = false,
|
|
||||||
configLoaded = false,
|
configLoaded = false,
|
||||||
}: UseWarningsProps) => {
|
}: UseWarningsProps) => {
|
||||||
const [backendWarnings, setBackendWarnings] = useState<Warning[]>([]);
|
const [backendWarnings, setBackendWarnings] = useState<Warning[]>([]);
|
||||||
|
|
@ -251,21 +218,14 @@ export const useWarnings = ({
|
||||||
window.electronAPI.kobold.getAvailableBackends(),
|
window.electronAPI.kobold.getAvailableBackends(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const cpuCapabilities = {
|
|
||||||
avx: cpuCapabilitiesResult.avx,
|
|
||||||
avx2: cpuCapabilitiesResult.avx2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await checkBackendWarnings({
|
const result = await checkBackendWarnings({
|
||||||
backend,
|
backend,
|
||||||
cpuCapabilities,
|
cpuCapabilities: cpuCapabilitiesResult,
|
||||||
noavx2,
|
|
||||||
failsafe,
|
|
||||||
availableBackends,
|
availableBackends,
|
||||||
});
|
});
|
||||||
|
|
||||||
setBackendWarnings(result);
|
setBackendWarnings(result);
|
||||||
}, [backend, noavx2, failsafe]);
|
}, [backend]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateBackendWarnings();
|
updateBackendWarnings();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/* eslint-disable no-comments/disallowComments */
|
/* eslint-disable no-comments/disallowComments */
|
||||||
import {
|
import {
|
||||||
cpu as siCpu,
|
cpu as siCpu,
|
||||||
cpuFlags,
|
|
||||||
mem as siMem,
|
mem as siMem,
|
||||||
memLayout as siMemLayout,
|
memLayout as siMemLayout,
|
||||||
} from 'systeminformation';
|
} from 'systeminformation';
|
||||||
|
|
@ -16,7 +15,11 @@ import type {
|
||||||
import { execa } from 'execa';
|
import { execa } from 'execa';
|
||||||
import { formatDeviceName } from '@/utils/format';
|
import { formatDeviceName } from '@/utils/format';
|
||||||
import { platform } from 'process';
|
import { platform } from 'process';
|
||||||
import { getVulkanInfo, detectLinuxGPUViaVulkan } from '@/utils/node/vulkan';
|
import {
|
||||||
|
getVulkanInfo,
|
||||||
|
detectLinuxGPUViaVulkan,
|
||||||
|
detectVulkan,
|
||||||
|
} from '@/utils/node/vulkan';
|
||||||
|
|
||||||
const COMMON_EXEC_OPTIONS = {
|
const COMMON_EXEC_OPTIONS = {
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
|
|
@ -34,7 +37,7 @@ export async function detectCPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await safeExecute(async () => {
|
const result = await safeExecute(async () => {
|
||||||
const [cpu, flags] = await Promise.all([siCpu(), cpuFlags()]);
|
const cpu = await siCpu();
|
||||||
|
|
||||||
const devices: string[] = [];
|
const devices: string[] = [];
|
||||||
if (cpu.brand) {
|
if (cpu.brand) {
|
||||||
|
|
@ -43,12 +46,7 @@ export async function detectCPU() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const avx = flags.includes('avx') || flags.includes('AVX');
|
|
||||||
const avx2 = flags.includes('avx2') || flags.includes('AVX2');
|
|
||||||
|
|
||||||
const capabilities = {
|
const capabilities = {
|
||||||
avx,
|
|
||||||
avx2,
|
|
||||||
devices,
|
devices,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,8 +55,6 @@ export async function detectCPU() {
|
||||||
}, 'CPU detection failed');
|
}, 'CPU detection failed');
|
||||||
|
|
||||||
const fallbackCapabilities = {
|
const fallbackCapabilities = {
|
||||||
avx: false,
|
|
||||||
avx2: false,
|
|
||||||
devices: [],
|
devices: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -74,15 +70,15 @@ export async function detectGPU() {
|
||||||
const result = await safeExecute(async () => {
|
const result = await safeExecute(async () => {
|
||||||
if (platform === 'linux') {
|
if (platform === 'linux') {
|
||||||
return detectLinuxGPUViaVulkan();
|
return detectLinuxGPUViaVulkan();
|
||||||
} else {
|
|
||||||
return detectGPUViaSI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return detectGPUViaSI();
|
||||||
}, 'GPU detection failed');
|
}, 'GPU detection failed');
|
||||||
|
|
||||||
const fallbackGPUInfo = {
|
const fallbackGPUInfo = {
|
||||||
hasAMD: false,
|
hasAMD: false,
|
||||||
hasNVIDIA: false,
|
hasNVIDIA: false,
|
||||||
gpuInfo: ['GPU detection failed'],
|
gpuInfo: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
basicGPUInfoCache = result || fallbackGPUInfo;
|
basicGPUInfoCache = result || fallbackGPUInfo;
|
||||||
|
|
@ -317,28 +313,8 @@ export async function detectROCm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detectVulkan() {
|
|
||||||
try {
|
|
||||||
const vulkanInfo = await getVulkanInfo();
|
|
||||||
|
|
||||||
const devices: string[] = [];
|
|
||||||
|
|
||||||
for (const gpu of vulkanInfo.discreteGPUs) {
|
|
||||||
devices.push(formatDeviceName(gpu.deviceName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
supported: devices.length > 0,
|
|
||||||
devices,
|
|
||||||
version: vulkanInfo.apiVersion || 'Unknown',
|
|
||||||
};
|
|
||||||
} catch {
|
|
||||||
return { supported: false, devices: [], version: 'Unknown' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseClInfoOutput(output: string) {
|
function parseClInfoOutput(output: string) {
|
||||||
const devices: string[] = [];
|
const devices: { name: string; isIntegrated: boolean }[] = [];
|
||||||
const lines = output.split('\n');
|
const lines = output.split('\n');
|
||||||
|
|
||||||
let currentPlatform = '';
|
let currentPlatform = '';
|
||||||
|
|
@ -353,9 +329,13 @@ function parseClInfoOutput(output: string) {
|
||||||
|
|
||||||
if (line.includes('Device Type:') && line.includes('GPU')) {
|
if (line.includes('Device Type:') && line.includes('GPU')) {
|
||||||
const deviceName = findDeviceNameInClInfo(lines, i);
|
const deviceName = findDeviceNameInClInfo(lines, i);
|
||||||
|
const computeUnits = findComputeUnitsInClInfo(lines, i);
|
||||||
|
|
||||||
if (deviceName && currentPlatform) {
|
if (deviceName && currentPlatform) {
|
||||||
devices.push(formatDeviceName(deviceName));
|
devices.push({
|
||||||
|
name: formatDeviceName(deviceName),
|
||||||
|
isIntegrated: !isDiscreteGPU(deviceName, computeUnits),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -389,6 +369,38 @@ function findDeviceNameInClInfo(lines: string[], startIndex: number) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findComputeUnitsInClInfo(lines: string[], startIndex: number) {
|
||||||
|
for (
|
||||||
|
let j = startIndex + 1;
|
||||||
|
j < Math.min(startIndex + 50, lines.length);
|
||||||
|
j++
|
||||||
|
) {
|
||||||
|
const nextLine = lines[j].trim();
|
||||||
|
if (nextLine.includes('Max compute units:')) {
|
||||||
|
const units = nextLine.split('Max compute units:')[1]?.trim();
|
||||||
|
return units ? parseInt(units, 10) : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDiscreteGPU(deviceName: string, computeUnits: number) {
|
||||||
|
const lowerName = deviceName.toLowerCase();
|
||||||
|
|
||||||
|
if (
|
||||||
|
lowerName.includes('radeon(tm) graphics') ||
|
||||||
|
lowerName.includes('intel')
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computeUnits <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async function detectCLBlast() {
|
async function detectCLBlast() {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa('clinfo', [], COMMON_EXEC_OPTIONS);
|
const { stdout } = await execa('clinfo', [], COMMON_EXEC_OPTIONS);
|
||||||
|
|
|
||||||
9
src/types/hardware.d.ts
vendored
9
src/types/hardware.d.ts
vendored
|
|
@ -1,6 +1,4 @@
|
||||||
export interface CPUCapabilities {
|
export interface CPUCapabilities {
|
||||||
avx: boolean;
|
|
||||||
avx2: boolean;
|
|
||||||
devices: string[];
|
devices: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +32,7 @@ export interface GPUCapabilities {
|
||||||
};
|
};
|
||||||
clblast: {
|
clblast: {
|
||||||
readonly supported: boolean;
|
readonly supported: boolean;
|
||||||
readonly devices: readonly string[];
|
readonly devices: readonly CLBlastDevice[];
|
||||||
readonly version?: string;
|
readonly version?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -45,6 +43,11 @@ export interface BasicGPUInfo {
|
||||||
gpuInfo: string[];
|
gpuInfo: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CLBlastDevice {
|
||||||
|
readonly name: string;
|
||||||
|
readonly isIntegrated: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface HardwareDetectionResult {
|
export interface HardwareDetectionResult {
|
||||||
readonly supported: boolean;
|
readonly supported: boolean;
|
||||||
readonly devices: readonly string[];
|
readonly devices: readonly string[];
|
||||||
|
|
|
||||||
5
src/types/index.d.ts
vendored
5
src/types/index.d.ts
vendored
|
|
@ -72,7 +72,10 @@ export interface SelectOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackendOption extends SelectOption {
|
export interface BackendOption extends SelectOption {
|
||||||
readonly devices?: readonly string[];
|
readonly devices?: readonly (
|
||||||
|
| string
|
||||||
|
| { name: string; isIntegrated: boolean }
|
||||||
|
)[];
|
||||||
readonly disabled?: boolean;
|
readonly disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
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';
|
|
||||||
import { graphics as siGraphics } from 'systeminformation';
|
import { graphics as siGraphics } from 'systeminformation';
|
||||||
|
import { formatDeviceName } from '../format';
|
||||||
|
|
||||||
interface CachedGPUInfo {
|
interface CachedGPUInfo {
|
||||||
devicePath: string;
|
devicePath: string;
|
||||||
|
|
@ -167,43 +167,17 @@ async function getLinuxGPUData() {
|
||||||
|
|
||||||
async function getWindowsGPUData() {
|
async function getWindowsGPUData() {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execa(
|
const graphics = await siGraphics();
|
||||||
'powershell',
|
|
||||||
[
|
const discreteControllers = graphics.controllers.filter(
|
||||||
'-Command',
|
(controller) => controller.vram && controller.vram >= 1024
|
||||||
`$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}$' };
|
|
||||||
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 -ge 1) {
|
|
||||||
Write-Output "$($props.DriverDesc)|$vramGB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
|
||||||
],
|
|
||||||
{
|
|
||||||
timeout: 10000,
|
|
||||||
reject: false,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!stdout.trim()) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpus: GPUData[] = [];
|
const gpus: GPUData[] = [];
|
||||||
const lines = stdout.trim().split('\n');
|
|
||||||
const seenVram = new Set<number>();
|
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const controller of discreteControllers) {
|
||||||
const parts = line.trim().split('|');
|
if (controller.vram) {
|
||||||
if (parts.length === 2) {
|
const vramGB = parseFloat((controller.vram / 1024).toFixed(2));
|
||||||
const vramGB = parseFloat(parts[1]);
|
|
||||||
if (vramGB > 0 && !seenVram.has(vramGB)) {
|
|
||||||
seenVram.add(vramGB);
|
|
||||||
gpus.push({
|
gpus.push({
|
||||||
usage: 0,
|
usage: 0,
|
||||||
memoryUsed: 0,
|
memoryUsed: 0,
|
||||||
|
|
@ -212,7 +186,6 @@ async function getWindowsGPUData() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return gpus;
|
return gpus;
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -250,10 +223,17 @@ export async function detectGPUViaSI() {
|
||||||
let hasNVIDIA = false;
|
let hasNVIDIA = false;
|
||||||
const gpuInfo: string[] = [];
|
const gpuInfo: string[] = [];
|
||||||
|
|
||||||
const discreteControllers = graphics.controllers.filter(
|
const discreteControllers = graphics.controllers.filter((controller) => {
|
||||||
(controller) =>
|
if (platform === 'linux' && controller.busAddress) {
|
||||||
controller.busAddress && isDiscreteBusAddress(controller.busAddress)
|
return isDiscreteBusAddress(controller.busAddress);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
if (platform === 'win32') {
|
||||||
|
return controller.vram && controller.vram >= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
for (const controller of discreteControllers) {
|
for (const controller of discreteControllers) {
|
||||||
if (
|
if (
|
||||||
|
|
@ -268,13 +248,13 @@ export async function detectGPUViaSI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller.model) {
|
if (controller.model) {
|
||||||
gpuInfo.push(controller.model);
|
gpuInfo.push(formatDeviceName(controller.model));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasAMD,
|
hasAMD,
|
||||||
hasNVIDIA,
|
hasNVIDIA,
|
||||||
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
gpuInfo: gpuInfo.length > 0 ? gpuInfo : [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,13 +151,33 @@ export async function detectLinuxGPUViaVulkan() {
|
||||||
return {
|
return {
|
||||||
hasAMD,
|
hasAMD,
|
||||||
hasNVIDIA,
|
hasNVIDIA,
|
||||||
gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
|
gpuInfo: gpuInfo.length > 0 ? gpuInfo : [],
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {
|
return {
|
||||||
hasAMD: false,
|
hasAMD: false,
|
||||||
hasNVIDIA: false,
|
hasNVIDIA: false,
|
||||||
gpuInfo: ['GPU detection failed'],
|
gpuInfo: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function detectVulkan() {
|
||||||
|
try {
|
||||||
|
const vulkanInfo = await getVulkanInfo();
|
||||||
|
|
||||||
|
const devices: string[] = [];
|
||||||
|
|
||||||
|
for (const gpu of vulkanInfo.discreteGPUs) {
|
||||||
|
devices.push(formatDeviceName(gpu.deviceName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
supported: devices.length > 0,
|
||||||
|
devices,
|
||||||
|
version: vulkanInfo.apiVersion || 'Unknown',
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return { supported: false, devices: [], version: 'Unknown' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
|
|
||||||
if (gpuCapabilities.cuda.driverVersion) {
|
if (gpuCapabilities.cuda.driverVersion) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'NVIDIA Driver',
|
label: 'NVIDIA',
|
||||||
value: gpuCapabilities.cuda.driverVersion,
|
value: gpuCapabilities.cuda.driverVersion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
|
||||||
|
|
||||||
if (gpuCapabilities.rocm.driverVersion) {
|
if (gpuCapabilities.rocm.driverVersion) {
|
||||||
items.push({
|
items.push({
|
||||||
label: 'AMD Driver',
|
label: 'AMD',
|
||||||
value: gpuCapabilities.rocm.driverVersion,
|
value: gpuCapabilities.rocm.driverVersion,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue