/** * The one entry point: compile a MATLAB script and run it on a GPUDevice. * * Compilation (numbl lowering + WGSL pipeline builds) is timed separately * from execution — the execution numbers are the ones to compare with MATLAB. */ import { compileScript } from './compile.ts'; import { planScript, type ScriptPlan } from './plan.ts'; import { executePlan, type RunResult } from './run.ts'; import { asCompileError } from './errors.ts'; export interface ScriptRun { compileSeconds: number; /** Human-readable op sequence, for the "what did this compile to" pane. */ planDescription: string[]; result: RunResult; } export interface SandboxGpu { device: GPUDevice; /** Human-readable adapter description, for the "ran on" line. */ description: string; } export async function requestSandboxDevice(): Promise { if (!navigator.gpu) { throw new Error('WebGPU is not available in this browser'); } const adapter = await navigator.gpu.requestAdapter(); if (!adapter) throw new Error('No WebGPU adapter available'); const want = (name: keyof GPUSupportedLimits): Record => ({ [name]: adapter.limits[name] as number, }); const device = await adapter.requestDevice({ requiredLimits: { ...want('maxStorageBufferBindingSize'), ...want('maxBufferSize'), ...want('maxStorageBuffersPerShaderStage'), }, }); const info = adapter.info; const description = [info?.description || info?.architecture, info?.vendor] .filter(Boolean) .join(' — ') || 'unknown adapter'; return { device, description }; } export async function runScript( device: GPUDevice, source: string, onOutput?: (text: string) => void, ): Promise { const t0 = performance.now(); let plan: ScriptPlan; try { const compiled = compileScript(source); plan = await planScript(device, compiled); } catch (e) { throw asCompileError(e); } const compileSeconds = (performance.now() - t0) / 1000; try { const result = await executePlan(device, plan, onOutput); return { compileSeconds, planDescription: plan.describe(), result }; } finally { plan.destroy(); } }