=> option !== null
+ );
+ }
+
+ return [
+ { value: 'all', label: 'All GPUs' },
+ ...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(),
+ label: `GPU ${index}: ${deviceName}`,
+ };
+ }),
+ ];
+ })();
return (
diff --git a/src/components/screens/Launch/index.tsx b/src/components/screens/Launch/index.tsx
index 5b3f96d..ed0b18c 100644
--- a/src/components/screens/Launch/index.tsx
+++ b/src/components/screens/Launch/index.tsx
@@ -75,8 +75,6 @@ export const LaunchScreen = ({ onLaunch }: LaunchScreenProps) => {
model,
sdmodel,
backend,
- noavx2,
- failsafe,
configLoaded,
});
diff --git a/src/hooks/useLaunchLogic.ts b/src/hooks/useLaunchLogic.ts
index 188efa5..0ca8bc0 100644
--- a/src/hooks/useLaunchLogic.ts
+++ b/src/hooks/useLaunchLogic.ts
@@ -85,10 +85,14 @@ const buildModelArgs = (
const buildConfigArgs = (isImageMode: boolean, launchArgs: LaunchArgs) => {
const args: string[] = [];
- if (launchArgs.autoGpuLayers) {
- args.push('--gpulayers', '-1');
- } else if (launchArgs.gpuLayers > 0) {
- args.push('--gpulayers', launchArgs.gpuLayers.toString());
+ const isGpuBackend = launchArgs.backend && launchArgs.backend !== 'cpu';
+
+ if (isGpuBackend) {
+ if (launchArgs.autoGpuLayers) {
+ args.push('--gpulayers', '-1');
+ } else if (launchArgs.gpuLayers > 0) {
+ args.push('--gpulayers', launchArgs.gpuLayers.toString());
+ }
}
if (launchArgs.contextSize) {
@@ -193,6 +197,9 @@ const buildBackendArgs = (launchArgs: LaunchArgs) => {
const args: string[] = [];
if (!launchArgs.backend || launchArgs.backend === 'cpu') {
+ if (launchArgs.backend === 'cpu') {
+ args.push('--usecpu');
+ }
return args;
}
diff --git a/src/hooks/useWarnings.ts b/src/hooks/useWarnings.ts
index f6945de..bf3fd1d 100644
--- a/src/hooks/useWarnings.ts
+++ b/src/hooks/useWarnings.ts
@@ -10,8 +10,6 @@ interface UseWarningsProps {
model: string;
sdmodel: string;
backend?: string;
- noavx2?: boolean;
- failsafe?: boolean;
configLoaded?: boolean;
}
@@ -133,9 +131,6 @@ const checkVramWarnings = async (backend: string): Promise => {
const checkCpuWarnings = (
backend: string,
- cpuCapabilities: { avx: boolean; avx2: boolean },
- noavx2: boolean,
- failsafe: boolean,
availableBackends: BackendOption[]
) => {
const warnings: Warning[] = [];
@@ -144,22 +139,6 @@ const checkCpuWarnings = (
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 (
availableBackends.length > 0 &&
availableBackends.some((b) => b.value === 'cpu')
@@ -177,11 +156,8 @@ const checkCpuWarnings = (
const checkBackendWarnings = async (params?: {
backend: string;
cpuCapabilities: {
- avx: boolean;
- avx2: boolean;
+ devices: string[];
} | null;
- noavx2: boolean;
- failsafe: boolean;
availableBackends: BackendOption[];
}): Promise => {
const warnings: Warning[] = [];
@@ -204,20 +180,13 @@ const checkBackendWarnings = async (params?: {
warnings.push(...gpuWarnings);
if (params) {
- const { backend, cpuCapabilities, noavx2, failsafe, availableBackends } =
- params;
+ const { backend, cpuCapabilities, availableBackends } = params;
const vramWarnings = await checkVramWarnings(backend);
warnings.push(...vramWarnings);
if (cpuCapabilities) {
- const cpuWarnings = checkCpuWarnings(
- backend,
- cpuCapabilities,
- noavx2,
- failsafe,
- availableBackends
- );
+ const cpuWarnings = checkCpuWarnings(backend, availableBackends);
warnings.push(...cpuWarnings);
}
}
@@ -229,8 +198,6 @@ export const useWarnings = ({
model,
sdmodel,
backend,
- noavx2 = false,
- failsafe = false,
configLoaded = false,
}: UseWarningsProps) => {
const [backendWarnings, setBackendWarnings] = useState([]);
@@ -251,21 +218,14 @@ export const useWarnings = ({
window.electronAPI.kobold.getAvailableBackends(),
]);
- const cpuCapabilities = {
- avx: cpuCapabilitiesResult.avx,
- avx2: cpuCapabilitiesResult.avx2,
- };
-
const result = await checkBackendWarnings({
backend,
- cpuCapabilities,
- noavx2,
- failsafe,
+ cpuCapabilities: cpuCapabilitiesResult,
availableBackends,
});
setBackendWarnings(result);
- }, [backend, noavx2, failsafe]);
+ }, [backend]);
useEffect(() => {
updateBackendWarnings();
diff --git a/src/main/modules/hardware.ts b/src/main/modules/hardware.ts
index 66fcf8f..04619ce 100644
--- a/src/main/modules/hardware.ts
+++ b/src/main/modules/hardware.ts
@@ -1,7 +1,6 @@
/* eslint-disable no-comments/disallowComments */
import {
cpu as siCpu,
- cpuFlags,
mem as siMem,
memLayout as siMemLayout,
} from 'systeminformation';
@@ -16,7 +15,11 @@ import type {
import { execa } from 'execa';
import { formatDeviceName } from '@/utils/format';
import { platform } from 'process';
-import { getVulkanInfo, detectLinuxGPUViaVulkan } from '@/utils/node/vulkan';
+import {
+ getVulkanInfo,
+ detectLinuxGPUViaVulkan,
+ detectVulkan,
+} from '@/utils/node/vulkan';
const COMMON_EXEC_OPTIONS = {
timeout: 3000,
@@ -34,7 +37,7 @@ export async function detectCPU() {
}
const result = await safeExecute(async () => {
- const [cpu, flags] = await Promise.all([siCpu(), cpuFlags()]);
+ const cpu = await siCpu();
const devices: string[] = [];
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 = {
- avx,
- avx2,
devices,
};
@@ -57,8 +55,6 @@ export async function detectCPU() {
}, 'CPU detection failed');
const fallbackCapabilities = {
- avx: false,
- avx2: false,
devices: [],
};
@@ -74,15 +70,15 @@ export async function detectGPU() {
const result = await safeExecute(async () => {
if (platform === 'linux') {
return detectLinuxGPUViaVulkan();
- } else {
- return detectGPUViaSI();
}
+
+ return detectGPUViaSI();
}, 'GPU detection failed');
const fallbackGPUInfo = {
hasAMD: false,
hasNVIDIA: false,
- gpuInfo: ['GPU detection failed'],
+ gpuInfo: [],
};
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) {
- const devices: string[] = [];
+ const devices: { name: string; isIntegrated: boolean }[] = [];
const lines = output.split('\n');
let currentPlatform = '';
@@ -353,9 +329,13 @@ function parseClInfoOutput(output: string) {
if (line.includes('Device Type:') && line.includes('GPU')) {
const deviceName = findDeviceNameInClInfo(lines, i);
+ const computeUnits = findComputeUnitsInClInfo(lines, i);
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 '';
}
+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() {
try {
const { stdout } = await execa('clinfo', [], COMMON_EXEC_OPTIONS);
diff --git a/src/types/hardware.d.ts b/src/types/hardware.d.ts
index dd246e1..050ee1a 100644
--- a/src/types/hardware.d.ts
+++ b/src/types/hardware.d.ts
@@ -1,6 +1,4 @@
export interface CPUCapabilities {
- avx: boolean;
- avx2: boolean;
devices: string[];
}
@@ -34,7 +32,7 @@ export interface GPUCapabilities {
};
clblast: {
readonly supported: boolean;
- readonly devices: readonly string[];
+ readonly devices: readonly CLBlastDevice[];
readonly version?: string;
};
}
@@ -45,6 +43,11 @@ export interface BasicGPUInfo {
gpuInfo: string[];
}
+export interface CLBlastDevice {
+ readonly name: string;
+ readonly isIntegrated: boolean;
+}
+
export interface HardwareDetectionResult {
readonly supported: boolean;
readonly devices: readonly string[];
diff --git a/src/types/index.d.ts b/src/types/index.d.ts
index dd8c362..117e74a 100644
--- a/src/types/index.d.ts
+++ b/src/types/index.d.ts
@@ -72,7 +72,10 @@ export interface SelectOption {
}
export interface BackendOption extends SelectOption {
- readonly devices?: readonly string[];
+ readonly devices?: readonly (
+ | string
+ | { name: string; isIntegrated: boolean }
+ )[];
readonly disabled?: boolean;
}
diff --git a/src/utils/node/gpu.ts b/src/utils/node/gpu.ts
index 4ffb486..a695cd2 100644
--- a/src/utils/node/gpu.ts
+++ b/src/utils/node/gpu.ts
@@ -1,8 +1,8 @@
import { readFile, readdir } from 'fs/promises';
import { join } from 'path';
import { platform } from 'process';
-import { execa } from 'execa';
import { graphics as siGraphics } from 'systeminformation';
+import { formatDeviceName } from '../format';
interface CachedGPUInfo {
devicePath: string;
@@ -167,50 +167,23 @@ async function getLinuxGPUData() {
async function getWindowsGPUData() {
try {
- const { stdout } = await execa(
- 'powershell',
- [
- '-Command',
- `$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,
- }
+ const graphics = await siGraphics();
+
+ const discreteControllers = graphics.controllers.filter(
+ (controller) => controller.vram && controller.vram >= 1024
);
- 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,
- });
- }
+ for (const controller of discreteControllers) {
+ if (controller.vram) {
+ const vramGB = parseFloat((controller.vram / 1024).toFixed(2));
+ gpus.push({
+ usage: 0,
+ memoryUsed: 0,
+ memoryTotal: vramGB,
+ temperature: undefined,
+ });
}
}
@@ -250,10 +223,17 @@ export async function detectGPUViaSI() {
let hasNVIDIA = false;
const gpuInfo: string[] = [];
- const discreteControllers = graphics.controllers.filter(
- (controller) =>
- controller.busAddress && isDiscreteBusAddress(controller.busAddress)
- );
+ const discreteControllers = graphics.controllers.filter((controller) => {
+ if (platform === 'linux' && controller.busAddress) {
+ return isDiscreteBusAddress(controller.busAddress);
+ }
+
+ if (platform === 'win32') {
+ return controller.vram && controller.vram >= 1024;
+ }
+
+ return false;
+ });
for (const controller of discreteControllers) {
if (
@@ -268,13 +248,13 @@ export async function detectGPUViaSI() {
}
if (controller.model) {
- gpuInfo.push(controller.model);
+ gpuInfo.push(formatDeviceName(controller.model));
}
}
return {
hasAMD,
hasNVIDIA,
- gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
+ gpuInfo: gpuInfo.length > 0 ? gpuInfo : [],
};
}
diff --git a/src/utils/node/vulkan.ts b/src/utils/node/vulkan.ts
index 8744364..737b4a6 100644
--- a/src/utils/node/vulkan.ts
+++ b/src/utils/node/vulkan.ts
@@ -151,13 +151,33 @@ export async function detectLinuxGPUViaVulkan() {
return {
hasAMD,
hasNVIDIA,
- gpuInfo: gpuInfo.length > 0 ? gpuInfo : ['No GPU information available'],
+ gpuInfo: gpuInfo.length > 0 ? gpuInfo : [],
};
} catch {
return {
hasAMD: 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' };
+ }
+}
diff --git a/src/utils/systemInfo.ts b/src/utils/systemInfo.ts
index dc81ef7..131a4d1 100644
--- a/src/utils/systemInfo.ts
+++ b/src/utils/systemInfo.ts
@@ -72,7 +72,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
if (gpuCapabilities.cuda.driverVersion) {
items.push({
- label: 'NVIDIA Driver',
+ label: 'NVIDIA',
value: gpuCapabilities.cuda.driverVersion,
});
}
@@ -88,7 +88,7 @@ export const createDriverItems = (hardwareInfo: HardwareInfo) => {
if (gpuCapabilities.rocm.driverVersion) {
items.push({
- label: 'AMD Driver',
+ label: 'AMD',
value: gpuCapabilities.rocm.driverVersion,
});
}