Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/components/TranscriptionModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -597,17 +597,21 @@ export default function TranscriptionModelPicker({
if (getCachedPlatform() === "darwin") return;
const detect = async () => {
try {
// Cards below the CUDA build's kernel floor (e.g. Pascal) crash at the
const [cuda, vulkan] = await Promise.all([
window.electronAPI?.getCudaWhisperStatus?.(),
window.electronAPI?.getVulkanWhisperStatus?.(),
]);
// Cards below the CUDA build's kernel floor (e.g. Maxwell) crash at the
// first kernel launch, so they get the Vulkan pack like AMD/Intel GPUs.
const cuda = await window.electronAPI?.getCudaWhisperStatus?.();
if (cuda?.gpuInfo.hasNvidiaGpu && cuda.gpuInfo.cudaSupported) {
const cudaEligible = !!cuda?.gpuInfo.hasNvidiaGpu && !!cuda.gpuInfo.cudaSupported;
// Prefer the pack that's already installed: a working Vulkan setup must
// not be re-prompted to download the CUDA pack (matches the resolver,
// which only prefers CUDA when it is actually downloaded).
if (cudaEligible && (cuda.downloaded || !vulkan?.downloaded)) {
setGpuBackend("cuda");
setGpuDownloaded(cuda.downloaded);
setGpuFailed(!!cuda.gpuFailed);
return;
}
const vulkan = await window.electronAPI?.getVulkanWhisperStatus?.();
if (vulkan?.vulkan.available) {
} else if (vulkan?.vulkan.available) {
setGpuBackend("vulkan");
setGpuDownloaded(vulkan.downloaded);
setGpuFailed(!!vulkan.gpuFailed);
Expand Down
15 changes: 8 additions & 7 deletions src/utils/gpuDetection.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
const { execFile } = require("child_process");

// The shipped CUDA whisper build only carries kernels for Turing (compute
// capability 7.5) and newer. On older cards (e.g. Pascal / GTX 10-series,
// 6.1) the server starts, loads the model into VRAM, then aborts on the first
// kernel launch with "no kernel image is available for execution on the
// device" — so those cards must be offered Vulkan instead, which works on any
// NVIDIA GPU.
const MIN_CUDA_COMPUTE_CAP = 7.5;
// The shipped CUDA whisper build (release 0.0.9) carries kernels for Pascal
// (compute capability 6.1) and newer; 6.1's embedded PTX also covers Volta via
// driver JIT. On cards below the floor the server starts, loads the model into
// VRAM, then aborts on the first kernel launch with "no kernel image is
// available for execution on the device" — so those cards must be offered
// Vulkan instead, which works on any NVIDIA GPU. Keep this floor in lockstep
// with CUDA_ARCHITECTURES in OpenWhispr/whisper.cpp's build-binaries.yml.
const MIN_CUDA_COMPUTE_CAP = 6.1;

let cachedGpuInfo = null;

Expand Down
15 changes: 11 additions & 4 deletions test/helpers/gpuDetection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ test("Turing and newer report cudaSupported", () => {
});
});

test("Pascal is detected but not CUDA-supported (gets Vulkan instead)", () => {
test("Pascal is CUDA-supported since the 0.0.9 build ships sm_61 kernels", () => {
const info = parseNvidiaSmiGpuInfo("NVIDIA GeForce GTX 1080 Ti, 582.66, 11264, 6.1");
assert.equal(info.hasNvidiaGpu, true);
assert.equal(info.computeCap, 6.1);
assert.equal(info.cudaSupported, true);
});

test("Maxwell is detected but not CUDA-supported (gets Vulkan instead)", () => {
const info = parseNvidiaSmiGpuInfo("NVIDIA GeForce GTX 970, 560.94, 4096, 5.2");
assert.equal(info.hasNvidiaGpu, true);
assert.equal(info.computeCap, 5.2);
assert.equal(info.cudaSupported, false);
});

test("exactly the minimum compute capability is supported", () => {
const info = parseNvidiaSmiGpuInfo(`RTX 2060, 560.94, 6144, ${MIN_CUDA_COMPUTE_CAP}`);
const info = parseNvidiaSmiGpuInfo(`Some GPU, 560.94, 6144, ${MIN_CUDA_COMPUTE_CAP}`);
assert.equal(info.cudaSupported, true);
});

Expand All @@ -46,9 +53,9 @@ test("an unparseable compute_cap ([N/A]) stays conservative", () => {

test("multi-GPU output uses the primary card", () => {
const info = parseNvidiaSmiGpuInfo(
"NVIDIA GeForce GTX 1080 Ti, 582.66, 11264, 6.1\nNVIDIA T400, 582.66, 2048, 7.5"
"NVIDIA GeForce GTX 970, 560.94, 4096, 5.2\nNVIDIA T400, 582.66, 2048, 7.5"
);
assert.equal(info.gpuName, "NVIDIA GeForce GTX 1080 Ti");
assert.equal(info.gpuName, "NVIDIA GeForce GTX 970");
assert.equal(info.cudaSupported, false);
});

Expand Down
Loading