concept-collection / random-points-in-disk
Report why the GPU path is unavailable instead of failing silently
Jeremy Magland <jmagland@flatironinstitute.org> committed commit ec70a1400dda parent 35a88f5 Browse files
1 changed file+28−6
index.htmlmodified+28−6View file
@@ -377,19 +377,36 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>,
377377 `;
378378
379379 let gpu = null;
380+let gpuReason = ''; // why the GPU path is unavailable, surfaced on the badge tooltip
380381 let seed = 1;
381382
382383 async function initGPU() {
383- if (!navigator.gpu) return null;
384+ if (!navigator.gpu) {
385+ gpuReason = 'navigator.gpu is undefined — this browser exposes no WebGPU.';
386+ console.warn('WebGPU: ' + gpuReason);
387+ return null;
388+ }
384389 try {
385- const adapter = await navigator.gpu.requestAdapter();
386- if (!adapter) return null;
390+ // A software adapter is still much faster than the JS path, so take one if offered.
391+ let adapter = await navigator.gpu.requestAdapter();
392+ if (!adapter) adapter = await navigator.gpu.requestAdapter({ forceFallbackAdapter: true });
393+ if (!adapter) {
394+ gpuReason = 'requestAdapter() returned null — no WebGPU adapter available. '
395+ + 'On Linux, Chrome needs its Vulkan backend: try enabling chrome://flags/#enable-unsafe-webgpu, '
396+ + 'and check chrome://gpu for the WebGPU and Vulkan rows.';
397+ console.warn('WebGPU: ' + gpuReason);
398+ return null;
399+ }
387400 const device = await adapter.requestDevice();
388401 const module = device.createShaderModule({ code: WGSL });
389402 if (module.getCompilationInfo) {
390403 const info = await module.getCompilationInfo();
391404 const errs = info.messages.filter((m) => m.type === 'error');
392- if (errs.length) { console.error('WGSL compilation failed', errs); return null; }
405+ if (errs.length) {
406+ gpuReason = 'the compute shader failed to compile; see the console.';
407+ console.error('WGSL compilation failed', errs);
408+ return null;
409+ }
393410 }
394411 const pipeline = device.createComputePipeline({
395412 layout: 'auto', compute: { module, entryPoint: 'main' },
@@ -409,6 +426,7 @@ async function initGPU() {
409426 device.lost.then(() => { gpu = null; showBackend(); });
410427 return { device, pipeline, params, counts, stage, bind, host: new Uint32Array(MAX_BINS) };
411428 } catch (e) {
429+ gpuReason = 'WebGPU initialization threw: ' + e;
412430 console.warn('WebGPU unavailable', e);
413431 return null;
414432 }
@@ -475,8 +493,12 @@ function showN(N) {
475493 const CPU_MAX = 750, GPU_MAX = 900; // slider units, = log10(N) * 100
476494
477495 function showBackend() {
478- el('backend').textContent = gpu ? 'WebGPU' : 'CPU';
479- el('backend').className = 'badge' + (gpu ? ' on' : '');
496+ const b = el('backend');
497+ b.textContent = gpu ? 'WebGPU' : 'CPU';
498+ b.className = 'badge' + (gpu ? ' on' : '');
499+ b.title = gpu
500+ ? 'Points are binned by a WebGPU compute shader.'
501+ : 'Binning on the CPU because ' + (gpuReason || 'the GPU path has not initialized yet.');
480502 const max = gpu ? GPU_MAX : CPU_MAX;
481503 nSlider.max = String(max);
482504 if (+nSlider.value > max) nSlider.value = String(max);