Custom WGSL DiT engine (int4, CFG-free 4-step)
Browse filesAdd an engine backend: the 22-block DiT runs on hand-written WebGPU
kernels (tiled/sgmat GEMM, fused LN+adaLN, RoPE, tiled QK/softmax/AV,
grouped convpos, GRN) with 4-bit packed weights and per-group scales
dequantized in-kernel. The worker loads only the ORT vocoder for this
path; ONNX Runtime remains the fallback.
- 229.7 MB int4 bundle, ~1.2 s per clip on WebGPU (was 2.8 s int4 ORT)
- flow: 4 steps, sway -1, cfg 0 (B=1, no null pass)
- tuning: tts.backend / tts.gemm switch kernels without touching the worker
- fixes found on the way: u32 vs f32 uniforms, second SiLU on the time MLP,
RoPE inv_freq exponent, AV score-tile column, f32 attention scores,
safe tanh in Mish
- engine/f5engine.js +1324 -0
- tts.worker.js +73 -13
- tuning.json +3 -1
engine/f5engine.js
ADDED
|
@@ -0,0 +1,1324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// F5-TTS DiT custom WebGPU engine (fp16 weights, fp32 accumulators).
|
| 2 |
+
const PRELUDE = `enable f16;\n`;
|
| 3 |
+
|
| 4 |
+
const REDUCE8 = `
|
| 5 |
+
if (tid < 128u) { red[tid] += red[tid + 128u]; } workgroupBarrier();
|
| 6 |
+
if (tid < 64u) { red[tid] += red[tid + 64u]; } workgroupBarrier();
|
| 7 |
+
if (tid < 32u) { red[tid] += red[tid + 32u]; } workgroupBarrier();
|
| 8 |
+
if (tid < 16u) { red[tid] += red[tid + 16u]; } workgroupBarrier();
|
| 9 |
+
if (tid < 8u) { red[tid] += red[tid + 8u]; } workgroupBarrier();
|
| 10 |
+
if (tid < 4u) { red[tid] += red[tid + 4u]; } workgroupBarrier();
|
| 11 |
+
if (tid < 2u) { red[tid] += red[tid + 2u]; } workgroupBarrier();
|
| 12 |
+
if (tid < 1u) { red[tid] += red[tid + 1u]; } workgroupBarrier();
|
| 13 |
+
`;
|
| 14 |
+
const REDUCE8MAX = `
|
| 15 |
+
if (tid < 128u) { red[tid] = max(red[tid], red[tid + 128u]); } workgroupBarrier();
|
| 16 |
+
if (tid < 64u) { red[tid] = max(red[tid], red[tid + 64u]); } workgroupBarrier();
|
| 17 |
+
if (tid < 32u) { red[tid] = max(red[tid], red[tid + 32u]); } workgroupBarrier();
|
| 18 |
+
if (tid < 16u) { red[tid] = max(red[tid], red[tid + 16u]); } workgroupBarrier();
|
| 19 |
+
if (tid < 8u) { red[tid] = max(red[tid], red[tid + 8u]); } workgroupBarrier();
|
| 20 |
+
if (tid < 4u) { red[tid] = max(red[tid], red[tid + 4u]); } workgroupBarrier();
|
| 21 |
+
if (tid < 2u) { red[tid] = max(red[tid], red[tid + 2u]); } workgroupBarrier();
|
| 22 |
+
if (tid < 1u) { red[tid] = max(red[tid], red[tid + 1u]); } workgroupBarrier();
|
| 23 |
+
`;
|
| 24 |
+
|
| 25 |
+
const GEMM = `
|
| 26 |
+
enable chromium_experimental_subgroup_matrix;
|
| 27 |
+
diagnostic(off, chromium.subgroup_matrix_uniformity);
|
| 28 |
+
struct P { M: u32, N: u32, K: u32, pad: u32 };
|
| 29 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 30 |
+
@group(0) @binding(1) var<storage, read> A: array<f16>;
|
| 31 |
+
@group(0) @binding(2) var<storage, read> B: array<f16>;
|
| 32 |
+
@group(0) @binding(3) var<storage, read> bias: array<f16>;
|
| 33 |
+
@group(0) @binding(4) var<storage, read_write> C: array<f16>;
|
| 34 |
+
const TR: u32 = 128u; const TC: u32 = 64u; const TK: u32 = 32u;
|
| 35 |
+
var<workgroup> tile_A: array<f16, TR * TK>;
|
| 36 |
+
var<workgroup> tile_B: array<f16, TC * TK>;
|
| 37 |
+
var<workgroup> scratch: array<array<f32, 64>, 8>;
|
| 38 |
+
@compute @workgroup_size(256)
|
| 39 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 40 |
+
let bm = wg.x * TR; let bn = wg.y * TC;
|
| 41 |
+
let sg = tid / 32u; let lane = tid % 32u;
|
| 42 |
+
let sg_row = sg / 2u; let sg_col = sg % 2u;
|
| 43 |
+
var acc: array<subgroup_matrix_result<f32, 8, 8>, 16>;
|
| 44 |
+
for (var i = 0u; i < 16u; i++) { acc[i] = subgroup_matrix_result<f32, 8, 8>(); }
|
| 45 |
+
let nk = min((p.K + TK - 1u) / TK, 2048u);
|
| 46 |
+
for (var kt = 0u; kt < nk; kt++) {
|
| 47 |
+
let k0 = kt * TK;
|
| 48 |
+
for (var i = 0u; i < 16u; i++) {
|
| 49 |
+
let idx = i * 256u + tid; let r = idx / TK; let c = idx % TK;
|
| 50 |
+
let gr = bm + r; let gc = k0 + c;
|
| 51 |
+
var v: f16 = f16(0.0);
|
| 52 |
+
if (gr < p.M && gc < p.K) { v = A[gr * p.K + gc]; }
|
| 53 |
+
tile_A[idx] = v;
|
| 54 |
+
}
|
| 55 |
+
for (var i = 0u; i < 8u; i++) {
|
| 56 |
+
let idx = i * 256u + tid; let gn = idx % TC; let gk = idx / TC;
|
| 57 |
+
let gr = bn + gn; let gc = k0 + gk;
|
| 58 |
+
var v: f16 = f16(0.0);
|
| 59 |
+
if (gr < p.N && gc < p.K) { v = B[gc * p.N + gr]; }
|
| 60 |
+
tile_B[gn * TK + gk] = v;
|
| 61 |
+
}
|
| 62 |
+
workgroupBarrier();
|
| 63 |
+
for (var step = 0u; step < TK; step += 8u) {
|
| 64 |
+
let a_off = (sg_row * 32u) * TK + step;
|
| 65 |
+
let m0 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off, false, TK);
|
| 66 |
+
let m1 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 8u * TK, false, TK);
|
| 67 |
+
let m2 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 16u * TK, false, TK);
|
| 68 |
+
let m3 = subgroupMatrixLoad<subgroup_matrix_left<f16, 8, 8>>(&tile_A, a_off + 24u * TK, false, TK);
|
| 69 |
+
let b_off = (sg_col * 32u) * TK + step;
|
| 70 |
+
let n0 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off, true, TK);
|
| 71 |
+
let n1 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 8u * TK, true, TK);
|
| 72 |
+
let n2 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 16u * TK, true, TK);
|
| 73 |
+
let n3 = subgroupMatrixLoad<subgroup_matrix_right<f16, 8, 8>>(&tile_B, b_off + 24u * TK, true, TK);
|
| 74 |
+
acc[0] = subgroupMatrixMultiplyAccumulate(m0, n0, acc[0]);
|
| 75 |
+
acc[1] = subgroupMatrixMultiplyAccumulate(m0, n1, acc[1]);
|
| 76 |
+
acc[2] = subgroupMatrixMultiplyAccumulate(m0, n2, acc[2]);
|
| 77 |
+
acc[3] = subgroupMatrixMultiplyAccumulate(m0, n3, acc[3]);
|
| 78 |
+
acc[4] = subgroupMatrixMultiplyAccumulate(m1, n0, acc[4]);
|
| 79 |
+
acc[5] = subgroupMatrixMultiplyAccumulate(m1, n1, acc[5]);
|
| 80 |
+
acc[6] = subgroupMatrixMultiplyAccumulate(m1, n2, acc[6]);
|
| 81 |
+
acc[7] = subgroupMatrixMultiplyAccumulate(m1, n3, acc[7]);
|
| 82 |
+
acc[8] = subgroupMatrixMultiplyAccumulate(m2, n0, acc[8]);
|
| 83 |
+
acc[9] = subgroupMatrixMultiplyAccumulate(m2, n1, acc[9]);
|
| 84 |
+
acc[10] = subgroupMatrixMultiplyAccumulate(m2, n2, acc[10]);
|
| 85 |
+
acc[11] = subgroupMatrixMultiplyAccumulate(m2, n3, acc[11]);
|
| 86 |
+
acc[12] = subgroupMatrixMultiplyAccumulate(m3, n0, acc[12]);
|
| 87 |
+
acc[13] = subgroupMatrixMultiplyAccumulate(m3, n1, acc[13]);
|
| 88 |
+
acc[14] = subgroupMatrixMultiplyAccumulate(m3, n2, acc[14]);
|
| 89 |
+
acc[15] = subgroupMatrixMultiplyAccumulate(m3, n3, acc[15]);
|
| 90 |
+
}
|
| 91 |
+
workgroupBarrier();
|
| 92 |
+
}
|
| 93 |
+
let row0 = bm + sg_row * 32u;
|
| 94 |
+
let col0 = bn + sg_col * 32u;
|
| 95 |
+
for (var f = 0u; f < 16u; f++) {
|
| 96 |
+
subgroupMatrixStore(&scratch[sg], 0u, acc[f], false, 8u);
|
| 97 |
+
workgroupBarrier();
|
| 98 |
+
let fr = f / 4u; let fc = f % 4u;
|
| 99 |
+
for (var e = lane; e < 64u; e += 32u) {
|
| 100 |
+
let i = e / 8u; let j = e % 8u;
|
| 101 |
+
let gr = row0 + fr * 8u + i; let gc = col0 + fc * 8u + j;
|
| 102 |
+
if (gr < p.M && gc < p.N) { C[gr * p.N + gc] = f16(clamp(scratch[sg][e] + f32(bias[gc]), -60000.0, 60000.0)); }
|
| 103 |
+
}
|
| 104 |
+
workgroupBarrier();
|
| 105 |
+
}
|
| 106 |
+
}
|
| 107 |
+
`;
|
| 108 |
+
|
| 109 |
+
const GEMM_SIMT_I4 = `
|
| 110 |
+
struct P { M: u32, N: u32, K: u32, group: u32 };
|
| 111 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 112 |
+
@group(0) @binding(1) var<storage, read> A: array<f16>;
|
| 113 |
+
@group(0) @binding(2) var<storage, read> Pk: array<u32>;
|
| 114 |
+
@group(0) @binding(3) var<storage, read> Sc: array<f16>;
|
| 115 |
+
@group(0) @binding(4) var<storage, read> bias: array<f16>;
|
| 116 |
+
@group(0) @binding(5) var<storage, read_write> C: array<f16>;
|
| 117 |
+
const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u;
|
| 118 |
+
var<workgroup> As: array<f32, 2048>;
|
| 119 |
+
var<workgroup> Bs: array<f32, 2048>;
|
| 120 |
+
@compute @workgroup_size(16, 16)
|
| 121 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 122 |
+
let bm = wg.x * BM; let bn = wg.y * BN;
|
| 123 |
+
let ty = tid / 16u; let tx = tid % 16u;
|
| 124 |
+
var acc: array<vec4<f32>, 4>;
|
| 125 |
+
for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); }
|
| 126 |
+
let nk = min((p.K + BK - 1u) / BK, 2048u);
|
| 127 |
+
for (var kt = 0u; kt < nk; kt++) {
|
| 128 |
+
let k0 = kt * BK;
|
| 129 |
+
for (var i = 0u; i < 8u; i++) {
|
| 130 |
+
let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK;
|
| 131 |
+
let gr = bm + r; let gc = k0 + c;
|
| 132 |
+
var v: f32 = 0.0;
|
| 133 |
+
if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); }
|
| 134 |
+
As[r * BK + c] = v;
|
| 135 |
+
}
|
| 136 |
+
// dequant B tile: 256 words (16 kp x 16 words of 4 columns)
|
| 137 |
+
let kp = tid / 16u; let n0 = (tid % 16u) * 4u;
|
| 138 |
+
let gk2 = k0 / 2u + kp; let gn0 = bn + n0;
|
| 139 |
+
var word: u32 = 0u;
|
| 140 |
+
if (gk2 < p.K / 2u && gn0 < p.N) { word = Pk[(gk2 * p.N + gn0) / 4u]; }
|
| 141 |
+
let grp = (k0 + 2u * kp) / p.group;
|
| 142 |
+
for (var j = 0u; j < 4u; j++) {
|
| 143 |
+
let n = n0 + j;
|
| 144 |
+
if (bn + n < p.N) {
|
| 145 |
+
let byte = (word >> (8u * j)) & 0xFFu;
|
| 146 |
+
let s = f32(Sc[grp * p.N + bn + n]);
|
| 147 |
+
Bs[(2u * kp) * BN + n] = (f32(byte & 0xFu) - 8.0) * s;
|
| 148 |
+
Bs[(2u * kp + 1u) * BN + n] = (f32(byte >> 4u) - 8.0) * s;
|
| 149 |
+
} else {
|
| 150 |
+
Bs[(2u * kp) * BN + n] = 0.0;
|
| 151 |
+
Bs[(2u * kp + 1u) * BN + n] = 0.0;
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
workgroupBarrier();
|
| 155 |
+
for (var k = 0u; k < BK; k++) {
|
| 156 |
+
let a0 = As[(ty * 4u + 0u) * BK + k];
|
| 157 |
+
let a1 = As[(ty * 4u + 1u) * BK + k];
|
| 158 |
+
let a2 = As[(ty * 4u + 2u) * BK + k];
|
| 159 |
+
let a3 = As[(ty * 4u + 3u) * BK + k];
|
| 160 |
+
let b0 = Bs[k * BN + tx * 4u + 0u];
|
| 161 |
+
let b1 = Bs[k * BN + tx * 4u + 1u];
|
| 162 |
+
let b2 = Bs[k * BN + tx * 4u + 2u];
|
| 163 |
+
let b3 = Bs[k * BN + tx * 4u + 3u];
|
| 164 |
+
let bv = vec4<f32>(b0, b1, b2, b3);
|
| 165 |
+
acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv;
|
| 166 |
+
}
|
| 167 |
+
workgroupBarrier();
|
| 168 |
+
}
|
| 169 |
+
for (var r = 0u; r < 4u; r++) {
|
| 170 |
+
let gr = bm + ty * 4u + r;
|
| 171 |
+
if (gr < p.M) {
|
| 172 |
+
for (var c = 0u; c < 4u; c++) {
|
| 173 |
+
let gc = bn + tx * 4u + c;
|
| 174 |
+
if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); }
|
| 175 |
+
}
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
`;
|
| 180 |
+
|
| 181 |
+
const GEMM_SIMT = `
|
| 182 |
+
struct P { M: u32, N: u32, K: u32, pad: u32 };
|
| 183 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 184 |
+
@group(0) @binding(1) var<storage, read> A: array<f16>;
|
| 185 |
+
@group(0) @binding(2) var<storage, read> B: array<f16>;
|
| 186 |
+
@group(0) @binding(3) var<storage, read> bias: array<f16>;
|
| 187 |
+
@group(0) @binding(4) var<storage, read_write> C: array<f16>;
|
| 188 |
+
const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u;
|
| 189 |
+
var<workgroup> As: array<f32, 2048>;
|
| 190 |
+
var<workgroup> Bs: array<f32, 2048>;
|
| 191 |
+
@compute @workgroup_size(16, 16)
|
| 192 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 193 |
+
let bm = wg.x * BM; let bn = wg.y * BN;
|
| 194 |
+
let ty = tid / 16u; let tx = tid % 16u;
|
| 195 |
+
var acc: array<vec4<f32>, 4>;
|
| 196 |
+
for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); }
|
| 197 |
+
let nk = min((p.K + BK - 1u) / BK, 2048u);
|
| 198 |
+
for (var kt = 0u; kt < nk; kt++) {
|
| 199 |
+
let k0 = kt * BK;
|
| 200 |
+
for (var i = 0u; i < 8u; i++) {
|
| 201 |
+
let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK;
|
| 202 |
+
let gr = bm + r; let gc = k0 + c;
|
| 203 |
+
var v: f32 = 0.0;
|
| 204 |
+
if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); }
|
| 205 |
+
As[r * BK + c] = v;
|
| 206 |
+
}
|
| 207 |
+
for (var i = 0u; i < 8u; i++) {
|
| 208 |
+
let idx = i * 256u + tid; let r = idx / BN; let c = idx % BN;
|
| 209 |
+
let gr = k0 + r; let gc = bn + c;
|
| 210 |
+
var v: f32 = 0.0;
|
| 211 |
+
if (gr < p.K && gc < p.N) { v = f32(B[gr * p.N + gc]); }
|
| 212 |
+
Bs[r * BN + c] = v;
|
| 213 |
+
}
|
| 214 |
+
workgroupBarrier();
|
| 215 |
+
for (var k = 0u; k < BK; k++) {
|
| 216 |
+
let a0 = As[(ty * 4u + 0u) * BK + k];
|
| 217 |
+
let a1 = As[(ty * 4u + 1u) * BK + k];
|
| 218 |
+
let a2 = As[(ty * 4u + 2u) * BK + k];
|
| 219 |
+
let a3 = As[(ty * 4u + 3u) * BK + k];
|
| 220 |
+
let b0 = Bs[k * BN + tx * 4u + 0u];
|
| 221 |
+
let b1 = Bs[k * BN + tx * 4u + 1u];
|
| 222 |
+
let b2 = Bs[k * BN + tx * 4u + 2u];
|
| 223 |
+
let b3 = Bs[k * BN + tx * 4u + 3u];
|
| 224 |
+
let bv = vec4<f32>(b0, b1, b2, b3);
|
| 225 |
+
acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv;
|
| 226 |
+
}
|
| 227 |
+
workgroupBarrier();
|
| 228 |
+
}
|
| 229 |
+
for (var r = 0u; r < 4u; r++) {
|
| 230 |
+
let gr = bm + ty * 4u + r;
|
| 231 |
+
if (gr < p.M) {
|
| 232 |
+
for (var c = 0u; c < 4u; c++) {
|
| 233 |
+
let gc = bn + tx * 4u + c;
|
| 234 |
+
if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); }
|
| 235 |
+
}
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
}
|
| 239 |
+
`;
|
| 240 |
+
|
| 241 |
+
const GEMV = `
|
| 242 |
+
struct P { N: u32, K: u32, pad0: u32, pad1: u32 };
|
| 243 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 244 |
+
@group(0) @binding(1) var<storage, read> A: array<f16>;
|
| 245 |
+
@group(0) @binding(2) var<storage, read> B: array<f16>;
|
| 246 |
+
@group(0) @binding(3) var<storage, read> bias: array<f16>;
|
| 247 |
+
@group(0) @binding(4) var<storage, read_write> C: array<f16>;
|
| 248 |
+
var<workgroup> shA: array<f32, 1024>;
|
| 249 |
+
@compute @workgroup_size(256)
|
| 250 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 251 |
+
for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); }
|
| 252 |
+
workgroupBarrier();
|
| 253 |
+
let base = wg.x * 1024u + tid * 4u;
|
| 254 |
+
var acc = vec4<f32>(0.0);
|
| 255 |
+
let KK = min(p.K, 65536u);
|
| 256 |
+
for (var k = 0u; k < KK; k++) {
|
| 257 |
+
let a = shA[k];
|
| 258 |
+
let o = k * p.N + base;
|
| 259 |
+
acc += a * vec4<f32>(f32(B[o]), f32(B[o + 1u]), f32(B[o + 2u]), f32(B[o + 3u]));
|
| 260 |
+
}
|
| 261 |
+
for (var j = 0u; j < 4u; j++) {
|
| 262 |
+
let c = base + j;
|
| 263 |
+
if (c < p.N) { C[c] = f16(acc[j] + f32(bias[c])); }
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
`;
|
| 267 |
+
|
| 268 |
+
const GEMV_I4 = `
|
| 269 |
+
struct P { N: u32, K: u32, group: u32, pad1: u32 };
|
| 270 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 271 |
+
@group(0) @binding(1) var<storage, read> A: array<f16>;
|
| 272 |
+
@group(0) @binding(2) var<storage, read> Pk: array<u32>;
|
| 273 |
+
@group(0) @binding(3) var<storage, read> Sc: array<f16>;
|
| 274 |
+
@group(0) @binding(4) var<storage, read> bias: array<f16>;
|
| 275 |
+
@group(0) @binding(5) var<storage, read_write> C: array<f16>;
|
| 276 |
+
var<workgroup> shA: array<f32, 1024>;
|
| 277 |
+
@compute @workgroup_size(256)
|
| 278 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 279 |
+
for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); }
|
| 280 |
+
workgroupBarrier();
|
| 281 |
+
let base = wg.x * 1024u + tid * 4u;
|
| 282 |
+
var acc = vec4<f32>(0.0);
|
| 283 |
+
let KK = min(p.K, 65536u);
|
| 284 |
+
for (var k = 0u; k < KK; k++) {
|
| 285 |
+
let a = shA[k];
|
| 286 |
+
let word = Pk[((k / 2u) * p.N + base) / 4u];
|
| 287 |
+
let sh = (k & 1u) * 4u;
|
| 288 |
+
let s = vec4<f32>(f32(Sc[(k / p.group) * p.N + base]), f32(Sc[(k / p.group) * p.N + base + 1u]),
|
| 289 |
+
f32(Sc[(k / p.group) * p.N + base + 2u]), f32(Sc[(k / p.group) * p.N + base + 3u]));
|
| 290 |
+
let q = vec4<f32>(f32((word >> sh) & 0xFu), f32((word >> (8u + sh)) & 0xFu),
|
| 291 |
+
f32((word >> (16u + sh)) & 0xFu), f32((word >> (24u + sh)) & 0xFu));
|
| 292 |
+
acc += a * (q - vec4<f32>(8.0)) * s;
|
| 293 |
+
}
|
| 294 |
+
let bb = vec4<f32>(f32(bias[base]), f32(bias[base + 1u]), f32(bias[base + 2u]), f32(bias[base + 3u]));
|
| 295 |
+
for (var j = 0u; j < 4u; j++) { if (base + j < p.N) { C[base + j] = f16(acc[j] + bb[j]); } }
|
| 296 |
+
}
|
| 297 |
+
`;
|
| 298 |
+
|
| 299 |
+
const LNM = `
|
| 300 |
+
struct P { M: u32, D: u32, offS: u32, offT: u32 };
|
| 301 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 302 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 303 |
+
@group(0) @binding(2) var<storage, read> MOD: array<f16>;
|
| 304 |
+
@group(0) @binding(3) var<storage, read_write> Y: array<f16>;
|
| 305 |
+
var<workgroup> red: array<f32, 256>;
|
| 306 |
+
var<workgroup> stats: array<f32, 2>;
|
| 307 |
+
@compute @workgroup_size(256)
|
| 308 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 309 |
+
let row = wg.x; let D = p.D; let off = row * D;
|
| 310 |
+
var s = 0.0;
|
| 311 |
+
for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); }
|
| 312 |
+
red[tid] = s; workgroupBarrier();
|
| 313 |
+
${REDUCE8}
|
| 314 |
+
if (tid == 0u) { stats[0] = red[0] / f32(D); }
|
| 315 |
+
workgroupBarrier();
|
| 316 |
+
let mu = stats[0];
|
| 317 |
+
var vs = 0.0;
|
| 318 |
+
for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; }
|
| 319 |
+
red[tid] = vs; workgroupBarrier();
|
| 320 |
+
${REDUCE8}
|
| 321 |
+
if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + 1e-6); }
|
| 322 |
+
workgroupBarrier();
|
| 323 |
+
let inv = stats[1];
|
| 324 |
+
for (var i = tid; i < D; i += 256u) {
|
| 325 |
+
let sc = 1.0 + f32(MOD[p.offS + i]);
|
| 326 |
+
let sh = f32(MOD[p.offT + i]);
|
| 327 |
+
Y[off + i] = f16((f32(X[off + i]) - mu) * inv * sc + sh);
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
`;
|
| 331 |
+
|
| 332 |
+
const LNA = `
|
| 333 |
+
struct P { M: u32, D: u32, pad0: u32, pad1: u32, eps: f32 };
|
| 334 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 335 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 336 |
+
@group(0) @binding(2) var<storage, read> Wt: array<f16>;
|
| 337 |
+
@group(0) @binding(3) var<storage, read> Bs: array<f16>;
|
| 338 |
+
@group(0) @binding(4) var<storage, read_write> Y: array<f16>;
|
| 339 |
+
var<workgroup> red: array<f32, 256>;
|
| 340 |
+
var<workgroup> stats: array<f32, 2>;
|
| 341 |
+
@compute @workgroup_size(256)
|
| 342 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 343 |
+
let row = wg.x; let D = p.D; let off = row * D;
|
| 344 |
+
var s = 0.0;
|
| 345 |
+
for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); }
|
| 346 |
+
red[tid] = s; workgroupBarrier();
|
| 347 |
+
${REDUCE8}
|
| 348 |
+
if (tid == 0u) { stats[0] = red[0] / f32(D); }
|
| 349 |
+
workgroupBarrier();
|
| 350 |
+
let mu = stats[0];
|
| 351 |
+
var vs = 0.0;
|
| 352 |
+
for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; }
|
| 353 |
+
red[tid] = vs; workgroupBarrier();
|
| 354 |
+
${REDUCE8}
|
| 355 |
+
if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + p.eps); }
|
| 356 |
+
workgroupBarrier();
|
| 357 |
+
let inv = stats[1];
|
| 358 |
+
for (var i = tid; i < D; i += 256u) {
|
| 359 |
+
Y[off + i] = f16((f32(X[off + i]) - mu) * inv * f32(Wt[i]) + f32(Bs[i]));
|
| 360 |
+
}
|
| 361 |
+
}
|
| 362 |
+
`;
|
| 363 |
+
|
| 364 |
+
const ROPE = `
|
| 365 |
+
struct P { M: u32, N: u32, total: u32, pad: u32 };
|
| 366 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 367 |
+
@group(0) @binding(1) var<storage, read> FREQ: array<f32>;
|
| 368 |
+
@group(0) @binding(2) var<storage, read_write> X: array<f16>;
|
| 369 |
+
const SC: f32 = 0.35355339059327379;
|
| 370 |
+
@compute @workgroup_size(256)
|
| 371 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 372 |
+
let i = gid.x;
|
| 373 |
+
if (i >= p.total) { return; }
|
| 374 |
+
let pair = i % 32u; let t = i / 32u; let h = t % 16u; let row = t / 16u;
|
| 375 |
+
let pos = row % p.N;
|
| 376 |
+
let theta = f32(pos) * FREQ[pair];
|
| 377 |
+
let c = cos(theta); let s = sin(theta);
|
| 378 |
+
let base = row * 1024u + h * 64u + pair * 2u;
|
| 379 |
+
let x0 = f32(X[base]); let x1 = f32(X[base + 1u]);
|
| 380 |
+
X[base] = f16((x0 * c - x1 * s) * SC);
|
| 381 |
+
X[base + 1u] = f16((x0 * s + x1 * c) * SC);
|
| 382 |
+
}
|
| 383 |
+
`;
|
| 384 |
+
|
| 385 |
+
const QK = `
|
| 386 |
+
struct P { N: u32, B: u32, pad0: u32, pad1: u32 };
|
| 387 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 388 |
+
@group(0) @binding(1) var<storage, read> Q: array<f16>;
|
| 389 |
+
@group(0) @binding(2) var<storage, read> K: array<f16>;
|
| 390 |
+
@group(0) @binding(3) var<storage, read_write> S: array<f32>;
|
| 391 |
+
var<workgroup> Qs: array<f16, 4096>;
|
| 392 |
+
var<workgroup> Ks: array<f16, 4096>;
|
| 393 |
+
@compute @workgroup_size(16, 16)
|
| 394 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 395 |
+
let b = wg.z / 16u; let h = wg.z % 16u;
|
| 396 |
+
let qi = wg.x * 64u; let kj = wg.y * 64u;
|
| 397 |
+
let N = min(p.N, 8192u);
|
| 398 |
+
for (var i = 0u; i < 16u; i++) {
|
| 399 |
+
let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u;
|
| 400 |
+
let qr = qi + r; var v: f16 = f16(0.0);
|
| 401 |
+
if (qr < N) { v = Q[((b * N + qr) * 16u + h) * 64u + c]; }
|
| 402 |
+
Qs[idx] = v;
|
| 403 |
+
}
|
| 404 |
+
for (var i = 0u; i < 16u; i++) {
|
| 405 |
+
let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u;
|
| 406 |
+
let kr = kj + r; var v: f16 = f16(0.0);
|
| 407 |
+
if (kr < N) { v = K[((b * N + kr) * 16u + h) * 64u + c]; }
|
| 408 |
+
Ks[idx] = v;
|
| 409 |
+
}
|
| 410 |
+
workgroupBarrier();
|
| 411 |
+
let ty = tid / 16u; let tx = tid % 16u;
|
| 412 |
+
var acc: array<vec4<f32>, 4>;
|
| 413 |
+
for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); }
|
| 414 |
+
for (var k = 0u; k < 64u; k++) {
|
| 415 |
+
let a0 = f32(Qs[(ty * 4u + 0u) * 64u + k]);
|
| 416 |
+
let a1 = f32(Qs[(ty * 4u + 1u) * 64u + k]);
|
| 417 |
+
let a2 = f32(Qs[(ty * 4u + 2u) * 64u + k]);
|
| 418 |
+
let a3 = f32(Qs[(ty * 4u + 3u) * 64u + k]);
|
| 419 |
+
let b0 = f32(Ks[(tx * 4u + 0u) * 64u + k]);
|
| 420 |
+
let b1 = f32(Ks[(tx * 4u + 1u) * 64u + k]);
|
| 421 |
+
let b2 = f32(Ks[(tx * 4u + 2u) * 64u + k]);
|
| 422 |
+
let b3 = f32(Ks[(tx * 4u + 3u) * 64u + k]);
|
| 423 |
+
let bv = vec4<f32>(b0, b1, b2, b3);
|
| 424 |
+
acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv;
|
| 425 |
+
}
|
| 426 |
+
let bidx = wg.z;
|
| 427 |
+
for (var r = 0u; r < 4u; r++) {
|
| 428 |
+
let gr = qi + ty * 4u + r;
|
| 429 |
+
if (gr < N) {
|
| 430 |
+
for (var c = 0u; c < 4u; c++) {
|
| 431 |
+
let gc = kj + tx * 4u + c;
|
| 432 |
+
if (gc < N) { S[(bidx * N + gr) * N + gc] = acc[r][c]; }
|
| 433 |
+
}
|
| 434 |
+
}
|
| 435 |
+
}
|
| 436 |
+
}
|
| 437 |
+
`;
|
| 438 |
+
|
| 439 |
+
const SOFTMAX = `
|
| 440 |
+
struct P { N: u32, rows: u32, pad0: u32, pad1: u32 };
|
| 441 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 442 |
+
@group(0) @binding(1) var<storage, read_write> S: array<f32>;
|
| 443 |
+
var<workgroup> red: array<f32, 256>;
|
| 444 |
+
var<workgroup> st: array<f32, 2>;
|
| 445 |
+
@compute @workgroup_size(256)
|
| 446 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 447 |
+
let off = wg.x * p.N;
|
| 448 |
+
var m = -3.4e38;
|
| 449 |
+
for (var i = tid; i < p.N; i += 256u) { m = max(m, S[off + i]); }
|
| 450 |
+
red[tid] = m; workgroupBarrier();
|
| 451 |
+
${REDUCE8MAX}
|
| 452 |
+
if (tid == 0u) { st[0] = red[0]; }
|
| 453 |
+
workgroupBarrier();
|
| 454 |
+
let mx = st[0];
|
| 455 |
+
var sum = 0.0;
|
| 456 |
+
for (var i = tid; i < p.N; i += 256u) { sum += exp(S[off + i] - mx); }
|
| 457 |
+
red[tid] = sum; workgroupBarrier();
|
| 458 |
+
${REDUCE8}
|
| 459 |
+
if (tid == 0u) { st[1] = 1.0 / red[0]; }
|
| 460 |
+
workgroupBarrier();
|
| 461 |
+
let inv = st[1];
|
| 462 |
+
for (var i = tid; i < p.N; i += 256u) { S[off + i] = exp(S[off + i] - mx) * inv; }
|
| 463 |
+
}
|
| 464 |
+
`;
|
| 465 |
+
|
| 466 |
+
const AV = `
|
| 467 |
+
struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 468 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 469 |
+
@group(0) @binding(1) var<storage, read> S: array<f32>;
|
| 470 |
+
@group(0) @binding(2) var<storage, read> V: array<f16>;
|
| 471 |
+
@group(0) @binding(3) var<storage, read_write> O: array<f16>;
|
| 472 |
+
var<workgroup> Ss: array<f32, 4096>;
|
| 473 |
+
var<workgroup> Vs: array<f16, 4096>;
|
| 474 |
+
@compute @workgroup_size(16, 16)
|
| 475 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 476 |
+
let b = wg.z / 16u; let h = wg.z % 16u;
|
| 477 |
+
let qi = wg.x * 64u;
|
| 478 |
+
let N = min(p.N, 8192u); let bidx = wg.z;
|
| 479 |
+
let ty = tid / 16u; let tx = tid % 16u;
|
| 480 |
+
var acc: array<vec4<f32>, 4>;
|
| 481 |
+
for (var i = 0u; i < 4u; i++) { acc[i] = vec4<f32>(0.0); }
|
| 482 |
+
let nk = min((N + 63u) / 64u, 256u);
|
| 483 |
+
for (var kt = 0u; kt < nk; kt++) {
|
| 484 |
+
let k0 = kt * 64u;
|
| 485 |
+
for (var i = 0u; i < 16u; i++) {
|
| 486 |
+
let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u;
|
| 487 |
+
let gr = qi + r; let skey = k0 + c; let vkey = k0 + r;
|
| 488 |
+
var v0: f32 = 0.0;
|
| 489 |
+
if (gr < N && skey < N) { v0 = S[(bidx * N + gr) * N + skey]; }
|
| 490 |
+
Ss[r * 64u + c] = v0;
|
| 491 |
+
var v1: f16 = f16(0.0);
|
| 492 |
+
if (vkey < N) { v1 = V[((b * N + vkey) * 16u + h) * 64u + c]; }
|
| 493 |
+
Vs[r * 64u + c] = v1;
|
| 494 |
+
}
|
| 495 |
+
workgroupBarrier();
|
| 496 |
+
for (var k = 0u; k < 64u; k++) {
|
| 497 |
+
let a0 = Ss[(ty * 4u + 0u) * 64u + k];
|
| 498 |
+
let a1 = Ss[(ty * 4u + 1u) * 64u + k];
|
| 499 |
+
let a2 = Ss[(ty * 4u + 2u) * 64u + k];
|
| 500 |
+
let a3 = Ss[(ty * 4u + 3u) * 64u + k];
|
| 501 |
+
let b0 = f32(Vs[k * 64u + tx * 4u + 0u]);
|
| 502 |
+
let b1 = f32(Vs[k * 64u + tx * 4u + 1u]);
|
| 503 |
+
let b2 = f32(Vs[k * 64u + tx * 4u + 2u]);
|
| 504 |
+
let b3 = f32(Vs[k * 64u + tx * 4u + 3u]);
|
| 505 |
+
let bv = vec4<f32>(b0, b1, b2, b3);
|
| 506 |
+
acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv;
|
| 507 |
+
}
|
| 508 |
+
workgroupBarrier();
|
| 509 |
+
}
|
| 510 |
+
for (var r = 0u; r < 4u; r++) {
|
| 511 |
+
let gr = qi + ty * 4u + r;
|
| 512 |
+
if (gr < N) {
|
| 513 |
+
for (var c = 0u; c < 4u; c++) {
|
| 514 |
+
O[((b * N + gr) * 16u + h) * 64u + tx * 4u + c] = f16(acc[r][c]);
|
| 515 |
+
}
|
| 516 |
+
}
|
| 517 |
+
}
|
| 518 |
+
}
|
| 519 |
+
`;
|
| 520 |
+
|
| 521 |
+
const ADDGATE = `
|
| 522 |
+
struct P { M: u32, D: u32, gOff: u32, pad: u32 };
|
| 523 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 524 |
+
@group(0) @binding(1) var<storage, read_write> X: array<f16>;
|
| 525 |
+
@group(0) @binding(2) var<storage, read> Z: array<f16>;
|
| 526 |
+
@group(0) @binding(3) var<storage, read> MOD: array<f16>;
|
| 527 |
+
@compute @workgroup_size(256)
|
| 528 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 529 |
+
let i = gid.x;
|
| 530 |
+
if (i >= p.M * p.D) { return; }
|
| 531 |
+
let d = i % p.D;
|
| 532 |
+
X[i] = f16(f32(X[i]) + f32(MOD[p.gOff + d]) * f32(Z[i]));
|
| 533 |
+
}
|
| 534 |
+
`;
|
| 535 |
+
|
| 536 |
+
const GELU = `
|
| 537 |
+
struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 538 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 539 |
+
@group(0) @binding(1) var<storage, read_write> X: array<f16>;
|
| 540 |
+
fn erf(x: f32) -> f32 {
|
| 541 |
+
let ax = abs(x);
|
| 542 |
+
let t = 1.0 / (1.0 + 0.3275911 * ax);
|
| 543 |
+
let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-ax * ax);
|
| 544 |
+
return select(-y, y, x >= 0.0);
|
| 545 |
+
}
|
| 546 |
+
@compute @workgroup_size(256)
|
| 547 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 548 |
+
if (gid.x >= p.total) { return; }
|
| 549 |
+
let x = f32(X[gid.x]);
|
| 550 |
+
X[gid.x] = f16(0.5 * x * (1.0 + erf(x * 0.70710678118654752)));
|
| 551 |
+
}
|
| 552 |
+
`;
|
| 553 |
+
|
| 554 |
+
const SILU = `
|
| 555 |
+
struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 556 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 557 |
+
@group(0) @binding(1) var<storage, read_write> X: array<f16>;
|
| 558 |
+
@compute @workgroup_size(256)
|
| 559 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 560 |
+
if (gid.x >= p.total) { return; }
|
| 561 |
+
let x = f32(X[gid.x]);
|
| 562 |
+
X[gid.x] = f16(x / (1.0 + exp(-x)));
|
| 563 |
+
}
|
| 564 |
+
`;
|
| 565 |
+
|
| 566 |
+
const MISH = `
|
| 567 |
+
struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 568 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 569 |
+
@group(0) @binding(1) var<storage, read_write> X: array<f16>;
|
| 570 |
+
fn tanh_safe(v: f32) -> f32 {
|
| 571 |
+
if (v > 15.0) { return 1.0; }
|
| 572 |
+
if (v < -15.0) { return -1.0; }
|
| 573 |
+
return tanh(v);
|
| 574 |
+
}
|
| 575 |
+
@compute @workgroup_size(256)
|
| 576 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 577 |
+
if (gid.x >= p.total) { return; }
|
| 578 |
+
let x = f32(X[gid.x]);
|
| 579 |
+
var sp = x;
|
| 580 |
+
if (x < 20.0) { sp = log(1.0 + exp(x)); }
|
| 581 |
+
X[gid.x] = f16(x * tanh_safe(sp));
|
| 582 |
+
}
|
| 583 |
+
`;
|
| 584 |
+
|
| 585 |
+
const CONCAT3 = `
|
| 586 |
+
struct P { N: u32, B: u32, pad0: u32, pad1: u32 };
|
| 587 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 588 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 589 |
+
@group(0) @binding(2) var<storage, read> C: array<f16>;
|
| 590 |
+
@group(0) @binding(3) var<storage, read> T: array<f16>;
|
| 591 |
+
@group(0) @binding(4) var<storage, read_write> O: array<f16>;
|
| 592 |
+
@compute @workgroup_size(256)
|
| 593 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 594 |
+
let i = gid.x;
|
| 595 |
+
if (i >= p.B * p.N * 712u) { return; }
|
| 596 |
+
let col = i % 712u; let row = i / 712u;
|
| 597 |
+
let b = row / p.N; let f = row % p.N;
|
| 598 |
+
var v: f16 = f16(0.0);
|
| 599 |
+
if (col < 100u) { v = X[f * 100u + col]; }
|
| 600 |
+
else if (col < 200u) { if (b == 0u) { v = C[f * 100u + (col - 100u)]; } }
|
| 601 |
+
else { if (b == 0u) { v = T[f * 512u + (col - 200u)]; } }
|
| 602 |
+
O[i] = v;
|
| 603 |
+
}
|
| 604 |
+
`;
|
| 605 |
+
|
| 606 |
+
const GATHER = `
|
| 607 |
+
struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 608 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 609 |
+
@group(0) @binding(1) var<storage, read> IDS: array<i32>;
|
| 610 |
+
@group(0) @binding(2) var<storage, read> EMB: array<f16>;
|
| 611 |
+
@group(0) @binding(3) var<storage, read> FRQ: array<f16>;
|
| 612 |
+
@group(0) @binding(4) var<storage, read_write> O: array<f16>;
|
| 613 |
+
@compute @workgroup_size(256)
|
| 614 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 615 |
+
let i = gid.x;
|
| 616 |
+
if (i >= p.N * 512u) { return; }
|
| 617 |
+
let f = i / 512u; let d = i % 512u;
|
| 618 |
+
let id = IDS[f] + 1;
|
| 619 |
+
if (id <= 0) { O[i] = f16(0.0); return; }
|
| 620 |
+
O[i] = f16(f32(EMB[u32(id) * 512u + d]) + f32(FRQ[f * 512u + d]));
|
| 621 |
+
}
|
| 622 |
+
`;
|
| 623 |
+
|
| 624 |
+
const DWCONV = `
|
| 625 |
+
struct P { M: u32, D: u32, pad0: u32, pad1: u32 };
|
| 626 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 627 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 628 |
+
@group(0) @binding(2) var<storage, read> Wt: array<f16>;
|
| 629 |
+
@group(0) @binding(3) var<storage, read> Bs: array<f16>;
|
| 630 |
+
@group(0) @binding(4) var<storage, read_write> Y: array<f16>;
|
| 631 |
+
@compute @workgroup_size(256)
|
| 632 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 633 |
+
let i = gid.x;
|
| 634 |
+
if (i >= p.M * p.D) { return; }
|
| 635 |
+
let d = i32(i % p.D); let f = i32(i / p.D);
|
| 636 |
+
var acc = f32(Bs[u32(d)]);
|
| 637 |
+
for (var k = 0; k < 7; k++) {
|
| 638 |
+
let ff = f - 3 + k;
|
| 639 |
+
if (ff >= 0 && ff < i32(p.M)) { acc += f32(Wt[u32(d) * 7u + u32(k)]) * f32(X[u32(ff) * p.D + u32(d)]); }
|
| 640 |
+
}
|
| 641 |
+
Y[i] = f16(acc);
|
| 642 |
+
}
|
| 643 |
+
`;
|
| 644 |
+
|
| 645 |
+
const GRNRED = `
|
| 646 |
+
struct P { M: u32, D: u32, off: u32, pad: u32 };
|
| 647 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 648 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 649 |
+
@group(0) @binding(2) var<storage, read_write> NM: array<f32>;
|
| 650 |
+
@compute @workgroup_size(256)
|
| 651 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>) {
|
| 652 |
+
let d = wg.x + p.off;
|
| 653 |
+
var s = 0.0;
|
| 654 |
+
for (var f = 0u; f < p.M; f++) { let v = f32(X[f * p.D + d]); s += v * v; }
|
| 655 |
+
NM[d] = sqrt(s);
|
| 656 |
+
}
|
| 657 |
+
`;
|
| 658 |
+
|
| 659 |
+
const GRNMEAN = `
|
| 660 |
+
struct P { D: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 661 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 662 |
+
@group(0) @binding(1) var<storage, read> NM: array<f32>;
|
| 663 |
+
@group(0) @binding(2) var<storage, read_write> MEAN: array<f32>;
|
| 664 |
+
var<workgroup> red: array<f32, 256>;
|
| 665 |
+
@compute @workgroup_size(256)
|
| 666 |
+
fn main(@builtin(local_invocation_index) tid: u32) {
|
| 667 |
+
var acc = 0.0;
|
| 668 |
+
for (var c = tid; c < p.D; c += 256u) { acc += NM[c]; }
|
| 669 |
+
red[tid] = acc; workgroupBarrier();
|
| 670 |
+
${REDUCE8}
|
| 671 |
+
if (tid == 0u) { MEAN[0] = red[0] / f32(p.D); }
|
| 672 |
+
}
|
| 673 |
+
`;
|
| 674 |
+
|
| 675 |
+
const GRNAPPLY = `
|
| 676 |
+
struct P { M: u32, D: u32, pad0: u32, pad1: u32 };
|
| 677 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 678 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 679 |
+
@group(0) @binding(2) var<storage, read_write> Y: array<f16>;
|
| 680 |
+
@group(0) @binding(3) var<storage, read> G: array<f16>;
|
| 681 |
+
@group(0) @binding(4) var<storage, read> B: array<f16>;
|
| 682 |
+
@group(0) @binding(5) var<storage, read> NM: array<f32>;
|
| 683 |
+
@group(0) @binding(6) var<storage, read> MEAN: array<f32>;
|
| 684 |
+
@compute @workgroup_size(256)
|
| 685 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 686 |
+
let i = gid.x;
|
| 687 |
+
if (i >= p.M * p.D) { return; }
|
| 688 |
+
let d = i % p.D;
|
| 689 |
+
let v = f32(X[i]);
|
| 690 |
+
Y[i] = f16(v * (NM[d] / (MEAN[0] + 1e-6)) * f32(G[d]) + f32(B[d]) + v);
|
| 691 |
+
}
|
| 692 |
+
`;
|
| 693 |
+
|
| 694 |
+
const ADDRES = `
|
| 695 |
+
struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 696 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 697 |
+
@group(0) @binding(1) var<storage, read_write> Y: array<f16>;
|
| 698 |
+
@group(0) @binding(2) var<storage, read> X: array<f16>;
|
| 699 |
+
@compute @workgroup_size(256)
|
| 700 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 701 |
+
if (gid.x >= p.total) { return; }
|
| 702 |
+
Y[gid.x] = f16(f32(Y[gid.x]) + f32(X[gid.x]));
|
| 703 |
+
}
|
| 704 |
+
`;
|
| 705 |
+
|
| 706 |
+
const ZEROFILL = `
|
| 707 |
+
struct P { M: u32, D: u32, pad0: u32, pad1: u32 };
|
| 708 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 709 |
+
@group(0) @binding(1) var<storage, read> IDS: array<i32>;
|
| 710 |
+
@group(0) @binding(2) var<storage, read_write> X: array<f16>;
|
| 711 |
+
@compute @workgroup_size(256)
|
| 712 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 713 |
+
let i = gid.x;
|
| 714 |
+
if (i >= p.M * p.D) { return; }
|
| 715 |
+
if (IDS[i / p.D] < 0) { X[i] = f16(0.0); }
|
| 716 |
+
}
|
| 717 |
+
`;
|
| 718 |
+
|
| 719 |
+
const CONVPOS = `
|
| 720 |
+
struct P { N: u32, M: u32, pad0: u32, pad1: u32 };
|
| 721 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 722 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 723 |
+
@group(0) @binding(2) var<storage, read> Wt: array<f16>;
|
| 724 |
+
@group(0) @binding(3) var<storage, read> Bs: array<f16>;
|
| 725 |
+
@group(0) @binding(4) var<storage, read_write> Y: array<f16>;
|
| 726 |
+
var<workgroup> Ws: array<f16, 2 * 64 * 31>;
|
| 727 |
+
var<workgroup> Xs: array<f32, 2 * 94>;
|
| 728 |
+
@compute @workgroup_size(256)
|
| 729 |
+
fn main(@builtin(workgroup_id) wg: vec3<u32>, @builtin(local_invocation_index) tid: u32) {
|
| 730 |
+
let g = wg.x / 16u; let pt = wg.x % 16u;
|
| 731 |
+
let p0 = pt * 64u;
|
| 732 |
+
let b = wg.y;
|
| 733 |
+
let tx = tid % 16u; let ty = tid / 16u;
|
| 734 |
+
let c0 = g * 64u + ty * 4u;
|
| 735 |
+
var acc: array<f32, 16>;
|
| 736 |
+
for (var i = 0u; i < 16u; i++) { acc[i] = 0.0; }
|
| 737 |
+
for (var cc = 0u; cc < 32u; cc++) {
|
| 738 |
+
let ci0 = cc * 2u;
|
| 739 |
+
for (var i = tid; i < 2u * 64u * 31u; i += 256u) {
|
| 740 |
+
let k = i % 31u; let oc = (i / 31u) % 64u; let ic = i / (31u * 64u);
|
| 741 |
+
Ws[i] = Wt[((g * 64u + oc) * 64u + (ci0 + ic)) * 31u + k];
|
| 742 |
+
}
|
| 743 |
+
for (var i = tid; i < 2u * 94u; i += 256u) {
|
| 744 |
+
let ic = i / 94u; let pp = i % 94u;
|
| 745 |
+
let pos = i32(p0) - 15 + i32(pp);
|
| 746 |
+
var v: f32 = 0.0;
|
| 747 |
+
if (pos >= 0 && pos < i32(p.N)) { v = f32(X[(b * p.N + u32(pos)) * 1024u + g * 64u + ci0 + ic]); }
|
| 748 |
+
Xs[i] = v;
|
| 749 |
+
}
|
| 750 |
+
workgroupBarrier();
|
| 751 |
+
for (var ic = 0u; ic < 2u; ic++) {
|
| 752 |
+
for (var k = 0u; k < 31u; k++) {
|
| 753 |
+
let x0 = Xs[ic * 94u + tx * 4u + k];
|
| 754 |
+
let x1 = Xs[ic * 94u + tx * 4u + 1u + k];
|
| 755 |
+
let x2 = Xs[ic * 94u + tx * 4u + 2u + k];
|
| 756 |
+
let x3 = Xs[ic * 94u + tx * 4u + 3u + k];
|
| 757 |
+
let w0 = f32(Ws[(ic * 64u + ty * 4u + 0u) * 31u + k]);
|
| 758 |
+
let w1 = f32(Ws[(ic * 64u + ty * 4u + 1u) * 31u + k]);
|
| 759 |
+
let w2 = f32(Ws[(ic * 64u + ty * 4u + 2u) * 31u + k]);
|
| 760 |
+
let w3 = f32(Ws[(ic * 64u + ty * 4u + 3u) * 31u + k]);
|
| 761 |
+
acc[0] += x0 * w0; acc[1] += x0 * w1; acc[2] += x0 * w2; acc[3] += x0 * w3;
|
| 762 |
+
acc[4] += x1 * w0; acc[5] += x1 * w1; acc[6] += x1 * w2; acc[7] += x1 * w3;
|
| 763 |
+
acc[8] += x2 * w0; acc[9] += x2 * w1; acc[10] += x2 * w2; acc[11] += x2 * w3;
|
| 764 |
+
acc[12] += x3 * w0; acc[13] += x3 * w1; acc[14] += x3 * w2; acc[15] += x3 * w3;
|
| 765 |
+
}
|
| 766 |
+
}
|
| 767 |
+
workgroupBarrier();
|
| 768 |
+
}
|
| 769 |
+
for (var oc = 0u; oc < 4u; oc++) {
|
| 770 |
+
for (var j = 0u; j < 4u; j++) {
|
| 771 |
+
let pos = p0 + tx * 4u + j;
|
| 772 |
+
if (pos < p.N) {
|
| 773 |
+
Y[(b * p.N + pos) * 1024u + c0 + oc] = f16(clamp(acc[j * 4u + oc] + f32(Bs[c0 + oc]), -60000.0, 60000.0));
|
| 774 |
+
}
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
}
|
| 778 |
+
`;
|
| 779 |
+
|
| 780 |
+
const FLOW = `
|
| 781 |
+
struct P { total: u32, N: u32, B: u32, pad1: u32, dt: f32 };
|
| 782 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 783 |
+
@group(0) @binding(1) var<storage, read_write> X: array<f16>;
|
| 784 |
+
@group(0) @binding(2) var<storage, read> V: array<f16>;
|
| 785 |
+
@compute @workgroup_size(256)
|
| 786 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 787 |
+
if (gid.x >= p.total) { return; }
|
| 788 |
+
let i = gid.x;
|
| 789 |
+
let d = i % 100u; let f = i / 100u;
|
| 790 |
+
let v0 = f32(V[f * 100u + d]);
|
| 791 |
+
var v = v0;
|
| 792 |
+
if (p.B == 2u) { let v1 = f32(V[(p.N + f) * 100u + d]); v = v0 + (v0 - v1) * 2.0; }
|
| 793 |
+
X[i] = f16(f32(X[i]) + p.dt * v);
|
| 794 |
+
}
|
| 795 |
+
`;
|
| 796 |
+
|
| 797 |
+
const TEB = `
|
| 798 |
+
struct P { N: u32, B: u32, pad0: u32, pad1: u32 };
|
| 799 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 800 |
+
@group(0) @binding(1) var<storage, read> T: array<f16>;
|
| 801 |
+
@group(0) @binding(2) var<storage, read_write> O: array<f16>;
|
| 802 |
+
@compute @workgroup_size(256)
|
| 803 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 804 |
+
let i = gid.x;
|
| 805 |
+
if (i >= p.B * p.N * 512u) { return; }
|
| 806 |
+
let row = i / 512u; let d = i % 512u;
|
| 807 |
+
if (row < p.N) { O[i] = T[row * 512u + d]; } else { O[i] = f16(0.0); }
|
| 808 |
+
}
|
| 809 |
+
`;
|
| 810 |
+
|
| 811 |
+
const COPY = `
|
| 812 |
+
struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 };
|
| 813 |
+
@group(0) @binding(0) var<uniform> p: P;
|
| 814 |
+
@group(0) @binding(1) var<storage, read> X: array<f16>;
|
| 815 |
+
@group(0) @binding(2) var<storage, read_write> Y: array<f16>;
|
| 816 |
+
@compute @workgroup_size(256)
|
| 817 |
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
| 818 |
+
if (gid.x >= p.total) { return; }
|
| 819 |
+
Y[gid.x] = X[gid.x];
|
| 820 |
+
}
|
| 821 |
+
`;
|
| 822 |
+
|
| 823 |
+
export const KERNELS = {
|
| 824 |
+
gemm: GEMM, gemv: GEMV, lnm: LNM, lna: LNA, rope: ROPE, qk: QK, softmax: SOFTMAX,
|
| 825 |
+
av: AV, addgate: ADDGATE, gelu: GELU, silu: SILU, mish: MISH, concat3: CONCAT3,
|
| 826 |
+
gather: GATHER, dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN, grnapply: GRNAPPLY,
|
| 827 |
+
addres: ADDRES, zerofill: ZEROFILL, convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY,
|
| 828 |
+
};
|
| 829 |
+
|
| 830 |
+
function f16tof32(h) {
|
| 831 |
+
const sign = (h & 0x8000) ? -1 : 1, exp = (h >> 10) & 0x1f, frac = h & 0x3ff;
|
| 832 |
+
if (exp === 0) return sign * Math.pow(2, -14) * (frac / 1024);
|
| 833 |
+
if (exp === 31) return frac ? NaN : sign * Infinity;
|
| 834 |
+
return sign * Math.pow(2, exp - 15) * (1 + frac / 1024);
|
| 835 |
+
}
|
| 836 |
+
|
| 837 |
+
export class F5Engine {
|
| 838 |
+
static async create(device, opts = {}) {
|
| 839 |
+
const e = new F5Engine(device, opts);
|
| 840 |
+
await e._init();
|
| 841 |
+
return e;
|
| 842 |
+
}
|
| 843 |
+
constructor(device, opts) { this.device = device; this.opts = opts; this.f32 = opts.dtype === 'f32'; this.es = this.f32 ? 4 : 2; }
|
| 844 |
+
|
| 845 |
+
async _init() {
|
| 846 |
+
const dev = this.device;
|
| 847 |
+
const [mr, br] = await Promise.all([
|
| 848 |
+
fetch(this.opts.metaUrl || '/webgpu/f5w.json'),
|
| 849 |
+
fetch(this.opts.binUrl || '/webgpu/f5w.bin'),
|
| 850 |
+
]);
|
| 851 |
+
this.meta = await mr.json();
|
| 852 |
+
this.binSize = Number(br.headers.get('content-length')) || 0;
|
| 853 |
+
this.wsize = Math.ceil((this.f32 ? this.binSize * 2 : this.binSize) / 256) * 256;
|
| 854 |
+
this._planChunks();
|
| 855 |
+
this.wbufs = this.chunkStarts.map((st, i) => {
|
| 856 |
+
const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize;
|
| 857 |
+
return dev.createBuffer({ size: Math.ceil((end - st) / 256) * 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
| 858 |
+
});
|
| 859 |
+
await this._streamWeights(br);
|
| 860 |
+
this.ubufs = [];
|
| 861 |
+
this.ubNext = 0;
|
| 862 |
+
this.compileErrors = [];
|
| 863 |
+
const cre = (name, code) => {
|
| 864 |
+
if (this.f32) code = code.replace(/enable f16;\n/g, '').replace(/\bf16\b/g, 'f32');
|
| 865 |
+
const mod = dev.createShaderModule({ code: (this.f32 ? '' : PRELUDE) + code });
|
| 866 |
+
mod.getCompilationInfo().then((ci) => {
|
| 867 |
+
for (const m of ci.messages) if (m.type === 'error') this.compileErrors.push(name + ': ' + m.message + ' @' + m.lineNum);
|
| 868 |
+
});
|
| 869 |
+
return dev.createComputePipelineAsync({ layout: 'auto', compute: { module: mod, entryPoint: 'main' } });
|
| 870 |
+
};
|
| 871 |
+
this.gemmKind = this.opts.gemm || (this.device.features.has('chromium-experimental-subgroup-matrix') ? 'sgmat' : 'simt');
|
| 872 |
+
console.log('DBG gemmKind', this.gemmKind, 'opts', JSON.stringify(Object.keys(this.opts || {})));
|
| 873 |
+
const defs = {
|
| 874 |
+
gemm: this.gemmKind === 'sgmat' ? GEMM : GEMM_SIMT, gemm_i4: GEMM_SIMT_I4, gemv: GEMV, gemv_i4: GEMV_I4, lnm: LNM, lna: LNA, rope: ROPE,
|
| 875 |
+
qk: QK, softmax: SOFTMAX, av: AV, addgate: ADDGATE, gelu: GELU,
|
| 876 |
+
silu: SILU, mish: MISH, concat3: CONCAT3, gather: GATHER,
|
| 877 |
+
dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN,
|
| 878 |
+
grnapply: GRNAPPLY, addres: ADDRES, zerofill: ZEROFILL,
|
| 879 |
+
convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY,
|
| 880 |
+
};
|
| 881 |
+
this.pipe = {};
|
| 882 |
+
for (const [name, code] of Object.entries(defs)) {
|
| 883 |
+
this.pipe[name] = await cre(name, code);
|
| 884 |
+
}
|
| 885 |
+
this.pipeNames = new Map(Object.entries(this.pipe).map(([k, v]) => [v, k]));
|
| 886 |
+
}
|
| 887 |
+
|
| 888 |
+
_planChunks() {
|
| 889 |
+
const mult = this.f32 ? 2 : 1;
|
| 890 |
+
const ents = Object.entries(this.meta).map(([n, t]) => {
|
| 891 |
+
if (t.q) return { n, off: t.packed * mult, len: (t.scales + t.scale_bytes) - t.packed };
|
| 892 |
+
const len = Math.ceil(t.shape.reduce((a, b) => a * b, 1) * this.es / 4) * 4;
|
| 893 |
+
return { n, off: t.offset * mult, len };
|
| 894 |
+
}).sort((a, b) => a.off - b.off);
|
| 895 |
+
const CHUNK = 640 << 20;
|
| 896 |
+
const starts = [0];
|
| 897 |
+
let cur = 0;
|
| 898 |
+
this.bufOf = new Map();
|
| 899 |
+
for (const e of ents) {
|
| 900 |
+
if (cur > 0 && cur + e.len > CHUNK) { starts.push(e.off); cur = 0; }
|
| 901 |
+
this.bufOf.set(e.n, starts.length - 1);
|
| 902 |
+
cur = e.off + e.len - starts[starts.length - 1];
|
| 903 |
+
}
|
| 904 |
+
this.chunkStarts = starts;
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
_writeAt(globalOff, data, byteLen) {
|
| 908 |
+
let off = globalOff, src = 0, remain = byteLen;
|
| 909 |
+
while (remain > 0) {
|
| 910 |
+
let bi = this.chunkStarts.length - 1;
|
| 911 |
+
for (let i = 0; i < this.chunkStarts.length; i++) {
|
| 912 |
+
const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize;
|
| 913 |
+
if (off < end) { bi = i; break; }
|
| 914 |
+
}
|
| 915 |
+
const base = this.chunkStarts[bi];
|
| 916 |
+
const bo = off - base;
|
| 917 |
+
const end = bi + 1 < this.chunkStarts.length ? this.chunkStarts[bi + 1] : this.wsize;
|
| 918 |
+
const n = Math.min(remain, end - off);
|
| 919 |
+
if (data instanceof Float32Array) {
|
| 920 |
+
this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src / 4, (src + n) / 4));
|
| 921 |
+
} else {
|
| 922 |
+
this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src, src + n));
|
| 923 |
+
}
|
| 924 |
+
off += n; src += n; remain -= n;
|
| 925 |
+
}
|
| 926 |
+
}
|
| 927 |
+
|
| 928 |
+
async _streamWeights(res) {
|
| 929 |
+
// Stream the bundle to the GPU in ~32 MB slices; convert f16 -> f32 if needed.
|
| 930 |
+
const dev = this.device;
|
| 931 |
+
const reader = res.body.getReader();
|
| 932 |
+
const CH = 32 << 20;
|
| 933 |
+
let stage = new Uint8Array(CH + 8);
|
| 934 |
+
let staged = 0, gpuOff = 0;
|
| 935 |
+
const flush = async (final) => {
|
| 936 |
+
if (!staged) return;
|
| 937 |
+
let n = staged;
|
| 938 |
+
const extra = final ? (4 - (n % 4)) % 4 : 0;
|
| 939 |
+
if (extra) { stage.fill(0, n, n + extra); n += extra; }
|
| 940 |
+
if (this.f32) {
|
| 941 |
+
const elems = n >> 1;
|
| 942 |
+
const out = new Float32Array(elems);
|
| 943 |
+
out.set(new Float16Array(stage.buffer, 0, elems));
|
| 944 |
+
this._writeAt(gpuOff, out, elems * 4);
|
| 945 |
+
gpuOff += elems * 4;
|
| 946 |
+
} else {
|
| 947 |
+
this._writeAt(gpuOff, stage.subarray(0, n), n);
|
| 948 |
+
gpuOff += n;
|
| 949 |
+
}
|
| 950 |
+
staged = 0;
|
| 951 |
+
if (this.opts.onProgress) this.opts.onProgress(gpuOff / (this.f32 ? this.binSize * 2 : this.binSize));
|
| 952 |
+
};
|
| 953 |
+
while (true) {
|
| 954 |
+
const { done, value } = await reader.read();
|
| 955 |
+
if (done) break;
|
| 956 |
+
let off = 0;
|
| 957 |
+
while (off < value.length) {
|
| 958 |
+
const take = Math.min(value.length - off, stage.length - staged);
|
| 959 |
+
stage.set(value.subarray(off, off + take), staged);
|
| 960 |
+
staged += take; off += take;
|
| 961 |
+
if (staged >= CH) await flush(false);
|
| 962 |
+
}
|
| 963 |
+
}
|
| 964 |
+
await flush(true);
|
| 965 |
+
const pad = Math.ceil(gpuOff / 256) * 256 - gpuOff;
|
| 966 |
+
if (pad > 0) this._writeAt(gpuOff, new Uint8Array(pad), pad);
|
| 967 |
+
}
|
| 968 |
+
|
| 969 |
+
_t(name) { const t = this.meta[name]; if (!t) throw new Error('missing ' + name); return t; }
|
| 970 |
+
_wslice(name) {
|
| 971 |
+
const t = this._t(name);
|
| 972 |
+
const n = t.shape.reduce((a, b) => a * b, 1);
|
| 973 |
+
const mult = this.f32 ? 2 : 1;
|
| 974 |
+
const bi = this.bufOf.get(name);
|
| 975 |
+
const base = this.chunkStarts[bi];
|
| 976 |
+
return { buffer: this.wbufs[bi], offset: t.offset * mult - base, size: Math.ceil(n * this.es / 4) * 4 };
|
| 977 |
+
}
|
| 978 |
+
_alc(bytes) {
|
| 979 |
+
const size = Math.ceil(bytes / 256) * 256;
|
| 980 |
+
return this.device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC });
|
| 981 |
+
}
|
| 982 |
+
_ub(vals, floats) {
|
| 983 |
+
const ab = new ArrayBuffer(256);
|
| 984 |
+
const dv = new DataView(ab);
|
| 985 |
+
for (let i = 0; i < vals.length; i++) {
|
| 986 |
+
if (floats && floats.indexOf(i) >= 0) dv.setFloat32(i * 4, vals[i], true);
|
| 987 |
+
else dv.setUint32(i * 4, vals[i] >>> 0, true);
|
| 988 |
+
}
|
| 989 |
+
const buf = this.device.createBuffer({ size: 256, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
| 990 |
+
this.device.queue.writeBuffer(buf, 0, ab);
|
| 991 |
+
this.ubNext++;
|
| 992 |
+
this.ubufs.push(buf);
|
| 993 |
+
return { buffer: buf, offset: 0, size: 256 };
|
| 994 |
+
}
|
| 995 |
+
_bg(pipe, u, bufs) {
|
| 996 |
+
const wrap = (b) => (b && b.buffer) ? b : { buffer: b, offset: 0, size: b.size };
|
| 997 |
+
return this.device.createBindGroup({
|
| 998 |
+
layout: pipe.getBindGroupLayout(0),
|
| 999 |
+
entries: [{ binding: 0, resource: wrap(u) }, ...bufs.map((b, i) => ({ binding: i + 1, resource: wrap(b) }))],
|
| 1000 |
+
});
|
| 1001 |
+
}
|
| 1002 |
+
|
| 1003 |
+
setup(N) {
|
| 1004 |
+
const dev = this.device;
|
| 1005 |
+
this.N = N; this.B = this.opts.branches || 2; this.M2 = this.B * N;
|
| 1006 |
+
const A = (name, elems) => (this[name] = this._alc(elems * this.es));
|
| 1007 |
+
A('xBuf', N * 100); A('condBuf', N * 100);
|
| 1008 |
+
A('teBuf', N * 512); A('tebBuf', 2 * N * 512); A('catBuf', 2 * N * 712);
|
| 1009 |
+
A('hBuf', 2 * N * 1024); A('tmpBuf', 2 * N * 1024); A('qBuf', 2 * N * 1024);
|
| 1010 |
+
A('kBuf', 2 * N * 1024); A('vBuf', 2 * N * 1024); A('attnBuf', 2 * N * 1024);
|
| 1011 |
+
A('ffBuf', 2 * N * 2048); A('velBuf', 2 * N * 100);
|
| 1012 |
+
A('timeIn', 256); A('tembBuf', 1024); A('temb2Buf', 1024);
|
| 1013 |
+
A('modBuf', 137216); this.scoresBuf = this._alc(this.B * 16 * N * N * 4);
|
| 1014 |
+
A('tBuf', N * 1024); A('tBuf2', N * 1024); A('tBuf3', N * 2048);
|
| 1015 |
+
A('tnmBuf', 1024 * 4); A('tmeanBuf', 4);
|
| 1016 |
+
this.idsBuf = this._alc(N * 4);
|
| 1017 |
+
this.ubNext = 0;
|
| 1018 |
+
this.ropeFreq = dev.createBuffer({ size: 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
| 1019 |
+
const f = new Float32Array(64);
|
| 1020 |
+
for (let j = 0; j < 32; j++) f[j] = Math.pow(10000, -j / 32);
|
| 1021 |
+
dev.queue.writeBuffer(this.ropeFreq, 0, f);
|
| 1022 |
+
this.calls = [];
|
| 1023 |
+
this._buildStepCalls();
|
| 1024 |
+
const stepCalls = this.calls;
|
| 1025 |
+
this.calls = [];
|
| 1026 |
+
this._buildTextCalls();
|
| 1027 |
+
this.textCalls = this.calls;
|
| 1028 |
+
this.calls = stepCalls;
|
| 1029 |
+
}
|
| 1030 |
+
|
| 1031 |
+
_add(pipe, bg, x, y = 1, z = 1, meta) {
|
| 1032 |
+
const c = { pipe, bg, x: Math.ceil(x), y, z, name: this.pipeNames.get(pipe), meta };
|
| 1033 |
+
this.calls.push(c);
|
| 1034 |
+
if (this.debug) this.debug.push(c);
|
| 1035 |
+
return c;
|
| 1036 |
+
}
|
| 1037 |
+
|
| 1038 |
+
_qdesc(name) {
|
| 1039 |
+
const t = this._t(name);
|
| 1040 |
+
const mult = this.f32 ? 2 : 1;
|
| 1041 |
+
const bi = this.bufOf.get(name);
|
| 1042 |
+
const base = this.chunkStarts[bi];
|
| 1043 |
+
return {
|
| 1044 |
+
packed: { buffer: this.wbufs[bi], offset: t.packed * mult - base, size: t.packed_bytes },
|
| 1045 |
+
scales: { buffer: this.wbufs[bi], offset: t.scales * mult - base, size: t.scale_bytes },
|
| 1046 |
+
group: t.group,
|
| 1047 |
+
};
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
_gemm(A, wn, bn, C, M, K, N) {
|
| 1051 |
+
const t = this._t(wn);
|
| 1052 |
+
if (t.q) {
|
| 1053 |
+
if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`);
|
| 1054 |
+
const q = this._qdesc(wn);
|
| 1055 |
+
if (q.packed.offset + q.packed.size > q.packed.buffer.size) throw new Error(`q4 packed OOB ${wn}`);
|
| 1056 |
+
const u = this._ub([M, N, K, q.group]);
|
| 1057 |
+
this._add(this.pipe.gemm_i4, this._bg(this.pipe.gemm_i4, u, [A, q.packed, q.scales, this._wslice(bn), C]),
|
| 1058 |
+
Math.ceil(M / 64), Math.ceil(N / 64));
|
| 1059 |
+
return;
|
| 1060 |
+
}
|
| 1061 |
+
const wa = this._wslice(wn), ba = this._wslice(bn);
|
| 1062 |
+
if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`);
|
| 1063 |
+
if (wa.offset + wa.size > wa.buffer.size) throw new Error(`gemm weight OOB ${wn}`);
|
| 1064 |
+
if (ba.offset + ba.size > ba.buffer.size) throw new Error(`gemm bias OOB ${bn}`);
|
| 1065 |
+
const u = this._ub([M, N, K, 0]);
|
| 1066 |
+
this._add(this.pipe.gemm, this._bg(this.pipe.gemm, u, [A, wa, ba, C]), Math.ceil(M / (this.gemmKind === 'sgmat' ? 128 : 64)), Math.ceil(N / 64), 1,
|
| 1067 |
+
{ vals: [M, N, K], wa: [wa.offset, wa.size, wa.buffer.size], ba: [ba.offset, ba.size, ba.buffer.size] });
|
| 1068 |
+
}
|
| 1069 |
+
_gemv(A, wn, bn, C, K, N) {
|
| 1070 |
+
const t = this._t(wn);
|
| 1071 |
+
if (t.q) {
|
| 1072 |
+
const q = this._qdesc(wn);
|
| 1073 |
+
const u = this._ub([N, K, q.group, 0]);
|
| 1074 |
+
this._add(this.pipe.gemv_i4, this._bg(this.pipe.gemv_i4, u, [A, q.packed, q.scales, this._wslice(bn), C]), Math.ceil(N / 1024));
|
| 1075 |
+
return;
|
| 1076 |
+
}
|
| 1077 |
+
const u = this._ub([N, K, 0, 0]);
|
| 1078 |
+
this._add(this.pipe.gemv, this._bg(this.pipe.gemv, u, [A, this._wslice(wn), this._wslice(bn), C]), Math.ceil(N / 1024));
|
| 1079 |
+
}
|
| 1080 |
+
_elem(pipe, X, total) {
|
| 1081 |
+
const u = this._ub([total, 0, 0, 0]);
|
| 1082 |
+
this._add(pipe, this._bg(pipe, u, [X]), Math.ceil(total / 256));
|
| 1083 |
+
}
|
| 1084 |
+
|
| 1085 |
+
_buildStepCalls() {
|
| 1086 |
+
const N = this.N, M2 = this.M2;
|
| 1087 |
+
this._gemm(this.timeIn, 'time.0', 'time.0.b', this.tembBuf, 1, 256, 1024);
|
| 1088 |
+
this._elem(this.pipe.silu, this.tembBuf, 1024);
|
| 1089 |
+
this._gemm(this.tembBuf, 'time.2', 'time.2.b', this.temb2Buf, 1, 1024, 1024);
|
| 1090 |
+
this._elem(this.pipe.silu, this.temb2Buf, 1024);
|
| 1091 |
+
this._gemv(this.temb2Buf, 'ada.w', 'ada.b', this.modBuf, 1024, 137216);
|
| 1092 |
+
this._add(this.pipe.concat3, this._bg(this.pipe.concat3, this._ub([N, this.B, 0, 0]), [this.xBuf, this.condBuf, this.tebBuf, this.catBuf]), Math.ceil(this.B * N * 712 / 256));
|
| 1093 |
+
this._gemm(this.catBuf, 'in.proj', 'in.proj.b', this.hBuf, M2, 712, 1024);
|
| 1094 |
+
const cp = (X, wn, bn, Y) => {
|
| 1095 |
+
const u = this._ub([N, M2, 0, 0]);
|
| 1096 |
+
this._add(this.pipe.convpos, this._bg(this.pipe.convpos, u, [X, this._wslice(wn), this._wslice(bn), Y]), 16 * 16, this.B);
|
| 1097 |
+
};
|
| 1098 |
+
cp(this.hBuf, 'convpos.0.w', 'convpos.0.b', this.tmpBuf);
|
| 1099 |
+
this._elem(this.pipe.mish, this.tmpBuf, M2 * 1024);
|
| 1100 |
+
cp(this.tmpBuf, 'convpos.2.w', 'convpos.2.b', this.qBuf);
|
| 1101 |
+
this._elem(this.pipe.mish, this.qBuf, M2 * 1024);
|
| 1102 |
+
{
|
| 1103 |
+
const u = this._ub([M2 * 1024, 0, 0, 0]);
|
| 1104 |
+
this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.hBuf, this.qBuf]), Math.ceil(M2 * 1024 / 256));
|
| 1105 |
+
}
|
| 1106 |
+
for (let L = 0; L < 22; L++) {
|
| 1107 |
+
const base = L * 6144;
|
| 1108 |
+
{
|
| 1109 |
+
const u = this._ub([M2, 1024, base + 1024, base]);
|
| 1110 |
+
this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2);
|
| 1111 |
+
}
|
| 1112 |
+
this._gemm(this.tmpBuf, `b${L}.q`, `b${L}.q.b`, this.qBuf, M2, 1024, 1024);
|
| 1113 |
+
this._gemm(this.tmpBuf, `b${L}.k`, `b${L}.k.b`, this.kBuf, M2, 1024, 1024);
|
| 1114 |
+
this._gemm(this.tmpBuf, `b${L}.v`, `b${L}.v.b`, this.vBuf, M2, 1024, 1024);
|
| 1115 |
+
for (const buf of [this.qBuf, this.kBuf]) {
|
| 1116 |
+
const u = this._ub([M2, N, M2 * 16 * 32, 0]);
|
| 1117 |
+
this._add(this.pipe.rope, this._bg(this.pipe.rope, u, [this.ropeFreq, buf]), Math.ceil(M2 * 16 * 32 / 256));
|
| 1118 |
+
}
|
| 1119 |
+
{
|
| 1120 |
+
const u = this._ub([N, 0, 0, 0]);
|
| 1121 |
+
this._add(this.pipe.qk, this._bg(this.pipe.qk, u, [this.qBuf, this.kBuf, this.scoresBuf]), Math.ceil(N / 64), Math.ceil(N / 64), this.B * 16);
|
| 1122 |
+
const u2 = this._ub([N, this.B * 16 * N, 0, 0]);
|
| 1123 |
+
this._add(this.pipe.softmax, this._bg(this.pipe.softmax, u2, [this.scoresBuf]), this.B * 16 * N);
|
| 1124 |
+
const u3 = this._ub([N, 0, 0, 0]);
|
| 1125 |
+
this._add(this.pipe.av, this._bg(this.pipe.av, u3, [this.scoresBuf, this.vBuf, this.attnBuf]), Math.ceil(N / 64), 1, this.B * 16);
|
| 1126 |
+
}
|
| 1127 |
+
this._gemm(this.attnBuf, `b${L}.o`, `b${L}.o.b`, this.tmpBuf, M2, 1024, 1024);
|
| 1128 |
+
{
|
| 1129 |
+
const u = this._ub([M2, 1024, base + 2048, 0]);
|
| 1130 |
+
this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256));
|
| 1131 |
+
}
|
| 1132 |
+
{
|
| 1133 |
+
const u = this._ub([M2, 1024, base + 4096, base + 3072]);
|
| 1134 |
+
this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2);
|
| 1135 |
+
}
|
| 1136 |
+
this._gemm(this.tmpBuf, `b${L}.up`, `b${L}.up.b`, this.ffBuf, M2, 1024, 2048);
|
| 1137 |
+
this._elem(this.pipe.gelu, this.ffBuf, M2 * 2048);
|
| 1138 |
+
this._gemm(this.ffBuf, `b${L}.down`, `b${L}.down.b`, this.tmpBuf, M2, 2048, 1024);
|
| 1139 |
+
{
|
| 1140 |
+
const u = this._ub([M2, 1024, base + 5120, 0]);
|
| 1141 |
+
this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256));
|
| 1142 |
+
}
|
| 1143 |
+
}
|
| 1144 |
+
const fbase = 22 * 6144;
|
| 1145 |
+
{
|
| 1146 |
+
const u = this._ub([M2, 1024, fbase, fbase + 1024]);
|
| 1147 |
+
this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2);
|
| 1148 |
+
}
|
| 1149 |
+
this._gemm(this.tmpBuf, 'out.w', 'out.b', this.velBuf, M2, 1024, 100);
|
| 1150 |
+
this.flowUniform = this._ub([N * 100, N, this.B, 0]);
|
| 1151 |
+
this._add(this.pipe.flow, this._bg(this.pipe.flow, this.flowUniform, [this.xBuf, this.velBuf]), Math.ceil(N * 100 / 256));
|
| 1152 |
+
}
|
| 1153 |
+
|
| 1154 |
+
_buildTextCalls() {
|
| 1155 |
+
const N = this.N;
|
| 1156 |
+
{
|
| 1157 |
+
const u = this._ub([N, 0, 0, 0]);
|
| 1158 |
+
this._add(this.pipe.gather, this._bg(this.pipe.gather, u, [this.idsBuf, this._wslice('txt.emb'), this._wslice('txt.freqs'), this.teBuf]), Math.ceil(N * 512 / 256));
|
| 1159 |
+
}
|
| 1160 |
+
for (let b = 0; b < 4; b++) {
|
| 1161 |
+
{
|
| 1162 |
+
const u = this._ub([N, 512, 0, 0]);
|
| 1163 |
+
this._add(this.pipe.dwconv, this._bg(this.pipe.dwconv, u, [this.teBuf, this._wslice(`tb${b}.dw.w`), this._wslice(`tb${b}.dw.b`), this.tBuf]), Math.ceil(N * 512 / 256));
|
| 1164 |
+
}
|
| 1165 |
+
{
|
| 1166 |
+
const u = this._ub([N, 512, 0, 0, 1e-5, 0, 0, 0], [4]);
|
| 1167 |
+
this._add(this.pipe.lna, this._bg(this.pipe.lna, u, [this.tBuf, this._wslice(`tb${b}.ln.w`), this._wslice(`tb${b}.ln.b`), this.tBuf3]), N);
|
| 1168 |
+
}
|
| 1169 |
+
this._gemm(this.tBuf3, `tb${b}.p1`, `tb${b}.p1.b`, this.tBuf2, N, 512, 1024);
|
| 1170 |
+
this._elem(this.pipe.gelu, this.tBuf2, N * 1024);
|
| 1171 |
+
{
|
| 1172 |
+
const u = this._ub([N, 1024, 0, 0]);
|
| 1173 |
+
this._add(this.pipe.grnred, this._bg(this.pipe.grnred, u, [this.tBuf2, this.tnmBuf]), 1024);
|
| 1174 |
+
const u2 = this._ub([1024, 0, 0, 0]);
|
| 1175 |
+
this._add(this.pipe.grnmean, this._bg(this.pipe.grnmean, u2, [this.tnmBuf, this.tmeanBuf]), 1);
|
| 1176 |
+
const u3 = this._ub([N, 1024, 0, 0]);
|
| 1177 |
+
this._add(this.pipe.grnapply, this._bg(this.pipe.grnapply, u3, [this.tBuf2, this.tBuf, this._wslice(`tb${b}.grn.g`), this._wslice(`tb${b}.grn.b`), this.tnmBuf, this.tmeanBuf]), Math.ceil(N * 1024 / 256));
|
| 1178 |
+
}
|
| 1179 |
+
this._gemm(this.tBuf, `tb${b}.p2`, `tb${b}.p2.b`, this.tBuf2, N, 1024, 512);
|
| 1180 |
+
{
|
| 1181 |
+
const u = this._ub([N * 512, 0, 0, 0]);
|
| 1182 |
+
this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256));
|
| 1183 |
+
const u2 = this._ub([N, 512, 0, 0]);
|
| 1184 |
+
this._add(this.pipe.zerofill, this._bg(this.pipe.zerofill, u2, [this.idsBuf, this.tBuf2]), Math.ceil(N * 512 / 256));
|
| 1185 |
+
const u3 = this._ub([N * 512, 0, 0, 0]);
|
| 1186 |
+
this._add(this.pipe.copy, this._bg(this.pipe.copy, u3, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256));
|
| 1187 |
+
}
|
| 1188 |
+
}
|
| 1189 |
+
{
|
| 1190 |
+
const u = this._ub([N, this.B, 0, 0]);
|
| 1191 |
+
this._add(this.pipe.teb, this._bg(this.pipe.teb, u, [this.teBuf, this.tebBuf]), Math.ceil(this.B * N * 512 / 256));
|
| 1192 |
+
}
|
| 1193 |
+
}
|
| 1194 |
+
|
| 1195 |
+
textNames() { return this.textCalls.map((c) => c.name); }
|
| 1196 |
+
async testGemm2(M, K, N, mode) {
|
| 1197 |
+
const dev = this.device;
|
| 1198 |
+
const mk = (bytes) => dev.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC });
|
| 1199 |
+
const A = mode === 'dedicated' ? mk(M * K * 2) : this.tBuf3;
|
| 1200 |
+
const C = mode === 'dedicated' ? mk(M * N * 2) : this.tBuf2;
|
| 1201 |
+
const W = mode === 'dedicated' ? { buffer: mk(K * N * 2), offset: 0, size: K * N * 2 } : this._wslice('tb0.p1');
|
| 1202 |
+
const Bs = mode === 'dedicated' ? { buffer: mk(N * 2), offset: 0, size: N * 2 } : this._wslice('tb0.p1.b');
|
| 1203 |
+
const u = this._ub([M, N, K, 0]);
|
| 1204 |
+
const bg = this._bg(this.pipe.gemm, u, [A, W, Bs, C]);
|
| 1205 |
+
this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 128), y: Math.ceil(N / 64), z: 1 }]);
|
| 1206 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1207 |
+
return mode + ' ok';
|
| 1208 |
+
}
|
| 1209 |
+
|
| 1210 |
+
async testGemm(M, K, N) {
|
| 1211 |
+
const u = this._ub([M, N, K, 0]);
|
| 1212 |
+
const bg = this._bg(this.pipe.gemm, u, [this.tBuf3, this._wslice('tb0.p1'), this._wslice('tb0.p1.b'), this.tBuf2]);
|
| 1213 |
+
this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 64), y: Math.ceil(N / 64), z: 1 }]);
|
| 1214 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1215 |
+
return M + 'x' + K + 'x' + N;
|
| 1216 |
+
}
|
| 1217 |
+
|
| 1218 |
+
debugText() { return this.textCalls.map((c) => ({ name: c.name, x: c.x, y: c.y, meta: c.meta })); }
|
| 1219 |
+
|
| 1220 |
+
async runTextPrefix(k) {
|
| 1221 |
+
this.encodeCalls(this.textCalls.slice(0, k));
|
| 1222 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1223 |
+
return k;
|
| 1224 |
+
}
|
| 1225 |
+
|
| 1226 |
+
async runOne(list, k) {
|
| 1227 |
+
this.encodeCalls([list[k]]);
|
| 1228 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1229 |
+
return list[k].name;
|
| 1230 |
+
}
|
| 1231 |
+
|
| 1232 |
+
async setCond(condMelF32, ids, limit) {
|
| 1233 |
+
const dev = this.device, N = this.N;
|
| 1234 |
+
const ca = this.f32 ? new Float32Array(N * 100) : new Float16Array(N * 100);
|
| 1235 |
+
ca.set(condMelF32.subarray(0, N * 100));
|
| 1236 |
+
dev.queue.writeBuffer(this.condBuf, 0, ca);
|
| 1237 |
+
const idarr = new Int32Array(N).fill(-1);
|
| 1238 |
+
for (let i = 0; i < Math.min(ids.length, N); i++) idarr[i] = ids[i];
|
| 1239 |
+
dev.queue.writeBuffer(this.idsBuf, 0, idarr);
|
| 1240 |
+
const list = (limit === undefined) ? this.textCalls : this.textCalls.slice(0, limit);
|
| 1241 |
+
if (list.length) this.encodeCalls(list);
|
| 1242 |
+
if (limit !== 0) await dev.queue.onSubmittedWorkDone();
|
| 1243 |
+
}
|
| 1244 |
+
|
| 1245 |
+
setX(xF32) {
|
| 1246 |
+
const f = this.f32 ? new Float32Array(this.N * 100) : new Float16Array(this.N * 100);
|
| 1247 |
+
f.set(xF32.subarray(0, this.N * 100));
|
| 1248 |
+
this.device.queue.writeBuffer(this.xBuf, 0, f);
|
| 1249 |
+
}
|
| 1250 |
+
|
| 1251 |
+
encodeCalls(list) {
|
| 1252 |
+
const enc = this.device.createCommandEncoder();
|
| 1253 |
+
const p = enc.beginComputePass();
|
| 1254 |
+
for (const c of list) {
|
| 1255 |
+
p.setPipeline(c.pipe);
|
| 1256 |
+
p.setBindGroup(0, c.bg);
|
| 1257 |
+
p.dispatchWorkgroups(c.x, c.y, c.z);
|
| 1258 |
+
}
|
| 1259 |
+
p.end();
|
| 1260 |
+
this.device.queue.submit([enc.finish()]);
|
| 1261 |
+
}
|
| 1262 |
+
|
| 1263 |
+
writeTime(t) {
|
| 1264 |
+
const half = 128;
|
| 1265 |
+
const tb = this.f32 ? new Float32Array(256) : new Float16Array(256);
|
| 1266 |
+
for (let j = 0; j < 128; j++) {
|
| 1267 |
+
const fr = Math.exp(-Math.log(10000) / (half - 1) * j);
|
| 1268 |
+
const a = t * 1000 * fr;
|
| 1269 |
+
tb[j] = Math.sin(a); tb[128 + j] = Math.cos(a);
|
| 1270 |
+
}
|
| 1271 |
+
this.device.queue.writeBuffer(this.timeIn, 0, tb);
|
| 1272 |
+
}
|
| 1273 |
+
|
| 1274 |
+
async step(t, dt) {
|
| 1275 |
+
this.writeTime(t);
|
| 1276 |
+
this.device.queue.writeBuffer(this.flowUniform.buffer, this.flowUniform.offset + 16, new Float32Array([dt]));
|
| 1277 |
+
this.encodeCalls(this.calls);
|
| 1278 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1279 |
+
}
|
| 1280 |
+
|
| 1281 |
+
async runStepPrefix(k, t) {
|
| 1282 |
+
this.writeTime(t === undefined ? 0.5 : t);
|
| 1283 |
+
this.encodeCalls(this.calls.slice(0, k));
|
| 1284 |
+
await this.device.queue.onSubmittedWorkDone();
|
| 1285 |
+
}
|
| 1286 |
+
|
| 1287 |
+
async readBuf(buf, elems) {
|
| 1288 |
+
const bytes = elems * this.es;
|
| 1289 |
+
const staging = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
| 1290 |
+
const enc = this.device.createCommandEncoder();
|
| 1291 |
+
enc.copyBufferToBuffer(buf, 0, staging, 0, bytes);
|
| 1292 |
+
this.device.queue.submit([enc.finish()]);
|
| 1293 |
+
await staging.mapAsync(GPUMapMode.READ);
|
| 1294 |
+
const ab = staging.getMappedRange().slice(0);
|
| 1295 |
+
staging.unmap();
|
| 1296 |
+
return this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab));
|
| 1297 |
+
}
|
| 1298 |
+
|
| 1299 |
+
async readVel() {
|
| 1300 |
+
const bytes = this.M2 * 100 * this.es;
|
| 1301 |
+
const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
| 1302 |
+
const enc = this.device.createCommandEncoder();
|
| 1303 |
+
enc.copyBufferToBuffer(this.velBuf, 0, buf, 0, bytes);
|
| 1304 |
+
this.device.queue.submit([enc.finish()]);
|
| 1305 |
+
await buf.mapAsync(GPUMapMode.READ);
|
| 1306 |
+
const ab = buf.getMappedRange().slice(0);
|
| 1307 |
+
const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab));
|
| 1308 |
+
buf.unmap();
|
| 1309 |
+
return out;
|
| 1310 |
+
}
|
| 1311 |
+
|
| 1312 |
+
async readX() {
|
| 1313 |
+
const bytes = this.N * 100 * this.es;
|
| 1314 |
+
const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
|
| 1315 |
+
const enc = this.device.createCommandEncoder();
|
| 1316 |
+
enc.copyBufferToBuffer(this.xBuf, 0, buf, 0, bytes);
|
| 1317 |
+
this.device.queue.submit([enc.finish()]);
|
| 1318 |
+
await buf.mapAsync(GPUMapMode.READ);
|
| 1319 |
+
const ab = buf.getMappedRange().slice(0);
|
| 1320 |
+
const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab));
|
| 1321 |
+
buf.unmap();
|
| 1322 |
+
return out;
|
| 1323 |
+
}
|
| 1324 |
+
}
|
tts.worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import * as ort from './ort/ort.mjs';
|
|
|
|
| 2 |
|
| 3 |
const SAMPLE_RATE = 24000;
|
| 4 |
const MEL_BINS = 100;
|
|
@@ -14,6 +15,8 @@ const MIN_GEN_FRAMES = 16;
|
|
| 14 |
const MAX_GEN_FRAMES = 640;
|
| 15 |
const TARGET_RMS = 0.1;
|
| 16 |
let refTextEncoder = null;
|
|
|
|
|
|
|
| 17 |
|
| 18 |
ort.env.wasm.wasmPaths = '/ort/';
|
| 19 |
ort.env.wasm.numThreads = Math.min(8, Math.max(1, (self.navigator?.hardwareConcurrency || 4) - 1));
|
|
@@ -121,7 +124,49 @@ async function createSession(url, label, executionProviders) {
|
|
| 121 |
return session;
|
| 122 |
}
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
async function loadGraphs() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
if (ditSession && vocoderSession) return;
|
| 126 |
const graphs = manifest.graphs || {};
|
| 127 |
const fp16Available = !!(graphs.dit_step_fp16 && graphs.vocoder_fp16);
|
|
@@ -151,7 +196,7 @@ async function loadGraphs() {
|
|
| 151 |
|
| 152 |
async function warmup() {
|
| 153 |
const frames = tuning?.tts?.warmupFrames ?? manifest.vocoder_chunk_frames ?? 375;
|
| 154 |
-
await runAndDispose(ditSession, () => ditInputs(null, frames, 16, null, null, null));
|
| 155 |
await runAndDispose(vocoderSession, () => ({ mel: f16Tensor(new Float32Array(MEL_BINS * frames), [1, MEL_BINS, frames]) }));
|
| 156 |
}
|
| 157 |
|
|
@@ -495,6 +540,7 @@ async function init(message) {
|
|
| 495 |
if (message.base) base = message.base;
|
| 496 |
await loadTuning();
|
| 497 |
manifest = await fetchJson(`${base}/manifest.json`);
|
|
|
|
| 498 |
await loadGraphs();
|
| 499 |
progress('warmup', { message: 'warming up kernels' });
|
| 500 |
await warmup();
|
|
@@ -514,7 +560,7 @@ async function getRefIds(voice) {
|
|
| 514 |
}
|
| 515 |
|
| 516 |
async function synthesize(message) {
|
| 517 |
-
if (!ditSession || !vocoderSession) throw new Error('worker not initialized');
|
| 518 |
const started = performance.now();
|
| 519 |
const { ids: genIds, unknown } = await tokenize(message.text);
|
| 520 |
if (!genIds.length) throw new Error(`no vocab tokens for: ${String(message.text).slice(0, 20)}`);
|
|
@@ -532,17 +578,31 @@ async function synthesize(message) {
|
|
| 532 |
cfgStrength = tuning?.tts?.cfgStrength ?? manifest.sampler?.cfg_strength ?? CFG_STRENGTH;
|
| 533 |
const steps = tuning?.tts?.flowSteps ?? manifest.sampler?.steps ?? DEFAULT_STEPS;
|
| 534 |
progress('synth', { message: 'flow matching', step: 0, steps });
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
frames
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
progress('synth', { message: 'vocoding', step: steps, steps });
|
| 547 |
const pcm = await decodeMel(mel.subarray(refFrames * MEL_BINS), genFrames);
|
| 548 |
let sum = 0;
|
|
|
|
| 1 |
import * as ort from './ort/ort.mjs';
|
| 2 |
+
import { F5Engine } from './engine/f5engine.js';
|
| 3 |
|
| 4 |
const SAMPLE_RATE = 24000;
|
| 5 |
const MEL_BINS = 100;
|
|
|
|
| 15 |
const MAX_GEN_FRAMES = 640;
|
| 16 |
const TARGET_RMS = 0.1;
|
| 17 |
let refTextEncoder = null;
|
| 18 |
+
let engine = null;
|
| 19 |
+
let engineDevice = null;
|
| 20 |
|
| 21 |
ort.env.wasm.wasmPaths = '/ort/';
|
| 22 |
ort.env.wasm.numThreads = Math.min(8, Math.max(1, (self.navigator?.hardwareConcurrency || 4) - 1));
|
|
|
|
| 124 |
return session;
|
| 125 |
}
|
| 126 |
|
| 127 |
+
async function initEngine() {
|
| 128 |
+
const want = (tuning?.tts?.backend ?? 'engine') === 'engine';
|
| 129 |
+
if (!want) return;
|
| 130 |
+
const info = await probeCaps();
|
| 131 |
+
if (!info.webgpu || !info.shaderF16) return;
|
| 132 |
+
try {
|
| 133 |
+
const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
|
| 134 |
+
if (!adapter) return;
|
| 135 |
+
const feats = ['shader-f16'];
|
| 136 |
+
let sgmat = false;
|
| 137 |
+
try { sgmat = adapter.features.has('chromium-experimental-subgroup-matrix'); } catch {}
|
| 138 |
+
if (sgmat) feats.push('chromium-experimental-subgroup-matrix');
|
| 139 |
+
const device = await adapter.requestDevice({
|
| 140 |
+
requiredFeatures: feats,
|
| 141 |
+
requiredLimits: {
|
| 142 |
+
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
|
| 143 |
+
maxBufferSize: adapter.limits.maxBufferSize,
|
| 144 |
+
maxComputeWorkgroupStorageSize: adapter.limits.maxComputeWorkgroupStorageSize,
|
| 145 |
+
},
|
| 146 |
+
});
|
| 147 |
+
engineDevice = device;
|
| 148 |
+
engine = await F5Engine.create(device, {
|
| 149 |
+
gemm: tuning?.tts?.gemm || (sgmat ? 'sgmat' : 'simt'),
|
| 150 |
+
branches: 1,
|
| 151 |
+
metaUrl: `${base}/f5w_q4.json`,
|
| 152 |
+
binUrl: `${base}/f5w_q4.bin`,
|
| 153 |
+
});
|
| 154 |
+
progress('session', { message: 'DiT kernels ready (WebGPU)' });
|
| 155 |
+
} catch (err) {
|
| 156 |
+
engine = null;
|
| 157 |
+
engineDevice = null;
|
| 158 |
+
progress('fallback', { message: `engine unavailable, using ORT: ${err?.message || err}` });
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
async function loadGraphs() {
|
| 163 |
+
if (engine) {
|
| 164 |
+
if (!vocoderSession) {
|
| 165 |
+
const g = manifest.graphs || {};
|
| 166 |
+
vocoderSession = await createSession(`${base}/${g.vocoder_fp16 || g.vocoder}`, 'vocoder · WebGPU', ['webgpu', 'wasm']);
|
| 167 |
+
}
|
| 168 |
+
return;
|
| 169 |
+
}
|
| 170 |
if (ditSession && vocoderSession) return;
|
| 171 |
const graphs = manifest.graphs || {};
|
| 172 |
const fp16Available = !!(graphs.dit_step_fp16 && graphs.vocoder_fp16);
|
|
|
|
| 196 |
|
| 197 |
async function warmup() {
|
| 198 |
const frames = tuning?.tts?.warmupFrames ?? manifest.vocoder_chunk_frames ?? 375;
|
| 199 |
+
if (!engine) await runAndDispose(ditSession, () => ditInputs(null, frames, 16, null, null, null));
|
| 200 |
await runAndDispose(vocoderSession, () => ({ mel: f16Tensor(new Float32Array(MEL_BINS * frames), [1, MEL_BINS, frames]) }));
|
| 201 |
}
|
| 202 |
|
|
|
|
| 540 |
if (message.base) base = message.base;
|
| 541 |
await loadTuning();
|
| 542 |
manifest = await fetchJson(`${base}/manifest.json`);
|
| 543 |
+
await initEngine();
|
| 544 |
await loadGraphs();
|
| 545 |
progress('warmup', { message: 'warming up kernels' });
|
| 546 |
await warmup();
|
|
|
|
| 560 |
}
|
| 561 |
|
| 562 |
async function synthesize(message) {
|
| 563 |
+
if ((!engine && !ditSession) || !vocoderSession) throw new Error('worker not initialized');
|
| 564 |
const started = performance.now();
|
| 565 |
const { ids: genIds, unknown } = await tokenize(message.text);
|
| 566 |
if (!genIds.length) throw new Error(`no vocab tokens for: ${String(message.text).slice(0, 20)}`);
|
|
|
|
| 578 |
cfgStrength = tuning?.tts?.cfgStrength ?? manifest.sampler?.cfg_strength ?? CFG_STRENGTH;
|
| 579 |
const steps = tuning?.tts?.flowSteps ?? manifest.sampler?.steps ?? DEFAULT_STEPS;
|
| 580 |
progress('synth', { message: 'flow matching', step: 0, steps });
|
| 581 |
+
let mel;
|
| 582 |
+
if (engine) {
|
| 583 |
+
engine.setup(frames);
|
| 584 |
+
await engine.setCond(padReference(voice, refFrames, frames), ids);
|
| 585 |
+
engine.setX(gaussianNoise(frames, MEL_BINS, message.seed ?? 1234));
|
| 586 |
+
await engineDevice.queue.onSubmittedWorkDone();
|
| 587 |
+
const sched = flowSchedule(steps, manifest.sampler?.sway_sampling_coef ?? -1);
|
| 588 |
+
for (let i = 0; i < steps; i++) {
|
| 589 |
+
progress('synth', { step: i + 1, steps, message: `flow step ${i + 1}/${steps}` });
|
| 590 |
+
await engine.step(sched[i], sched[i + 1] - sched[i]);
|
| 591 |
+
}
|
| 592 |
+
mel = await engine.readX();
|
| 593 |
+
} else {
|
| 594 |
+
mel = await flowSample(
|
| 595 |
+
(x, time) => ditStep(x, time, voice, ids, textLen, frames, refFrames),
|
| 596 |
+
frames,
|
| 597 |
+
MEL_BINS,
|
| 598 |
+
{
|
| 599 |
+
steps,
|
| 600 |
+
sway: manifest.sampler?.sway_sampling_coef ?? -1,
|
| 601 |
+
seed: message.seed ?? 1234,
|
| 602 |
+
onStep: (step, total) => progress('synth', { step, steps: total, message: `flow step ${step}/${total}` }),
|
| 603 |
+
},
|
| 604 |
+
);
|
| 605 |
+
}
|
| 606 |
progress('synth', { message: 'vocoding', step: steps, steps });
|
| 607 |
const pcm = await decodeMel(mel.subarray(refFrames * MEL_BINS), genFrames);
|
| 608 |
let sum = 0;
|
tuning.json
CHANGED
|
@@ -9,7 +9,9 @@
|
|
| 9 |
"tts": {
|
| 10 |
"flowSteps": 4,
|
| 11 |
"cfgStrength": 0,
|
| 12 |
-
"warmupFrames": 64
|
|
|
|
|
|
|
| 13 |
},
|
| 14 |
"session": {
|
| 15 |
"preferPrecision": "auto"
|
|
|
|
| 9 |
"tts": {
|
| 10 |
"flowSteps": 4,
|
| 11 |
"cfgStrength": 0,
|
| 12 |
+
"warmupFrames": 64,
|
| 13 |
+
"backend": "engine",
|
| 14 |
+
"gemm": "simt"
|
| 15 |
},
|
| 16 |
"session": {
|
| 17 |
"preferPrecision": "auto"
|