shaders/*.wgsl — one WGSL compute shader per BLAS routine (sscal, saxpy, sdot, …).
routineShaders below is the single source of truth: routine name → the WGSL source(s)
its getPipeline() calls actually reference, verified against every src/<routine>/<routine>.mjs
rather than inferred from naming convention (see its doc comment for the exceptions). Each
shader is imported right above the line that adds it — the import is the mapping entry, no
separate block to cross-reference. shaderSources, the flat name → source registry the
browser bundle's runtime lookup needs, is derived from routineShaders rather than
hand-duplicated, so the two can never drift apart. In Node.js neither is read — shaders are
readFileSync from disk directly; scripts/build-browser.mjs inlines this module into the
browser's IIFE bundle via esbuild instead.
Cross-shader patterns
Single bind group. Every shader with bindings uses @group(0) only — the JS side always
calls pipeline.getBindGroupLayout(0), no secondary groups to track. Binding order is
consistent too: any read-only storage buffers come before read_write ones, with the
uniform Params struct always last. @binding indices match the position of each resource in
the array passed to createBindGroup, which appends resultBuffer last.
All counts and strides are u32.n, x_inc, y_inc, and every other index/count field
in a Params struct is unsigned, avoiding implicit sign-extension in index expressions like
id * params.x_inc.
Entry points don't have to be named main.loadShader (util/pipeline.mjs)
auto-detects the sole @compute function in a module instead of requiring a fixed name, so
dasum_main, strsv_invert_block_main, etc. work without renaming.
Structure
shaders/*.wgsl— one WGSL compute shader per BLAS routine (sscal, saxpy, sdot, …).routineShadersbelow is the single source of truth: routine name → the WGSL source(s) itsgetPipeline()calls actually reference, verified against everysrc/<routine>/<routine>.mjsrather than inferred from naming convention (see its doc comment for the exceptions). Each shader is imported right above the line that adds it — the import is the mapping entry, no separate block to cross-reference.shaderSources, the flat name → source registry the browser bundle's runtime lookup needs, is derived fromroutineShadersrather than hand-duplicated, so the two can never drift apart. In Node.js neither is read — shaders arereadFileSyncfrom disk directly;scripts/build-browser.mjsinlines this module into the browser's IIFE bundle via esbuild instead.Cross-shader patterns
Single bind group. Every shader with bindings uses
@group(0)only — the JS side always callspipeline.getBindGroupLayout(0), no secondary groups to track. Binding order is consistent too: any read-only storage buffers come before read_write ones, with theuniform Paramsstruct always last.@bindingindices match the position of each resource in the array passed tocreateBindGroup, which appendsresultBufferlast.All counts and strides are
u32.n,x_inc,y_inc, and every other index/count field in aParamsstruct is unsigned, avoiding implicit sign-extension in index expressions likeid * params.x_inc.Entry points don't have to be named
main.loadShader(util/pipeline.mjs) auto-detects the sole@computefunction in a module instead of requiring a fixed name, sodasum_main,strsv_invert_block_main, etc. work without renaming.