// F5-TTS DiT custom WebGPU engine (fp16 weights, fp32 accumulators). const PRELUDE = `enable f16;\n`; const REDUCE8 = ` if (tid < 128u) { red[tid] += red[tid + 128u]; } workgroupBarrier(); if (tid < 64u) { red[tid] += red[tid + 64u]; } workgroupBarrier(); if (tid < 32u) { red[tid] += red[tid + 32u]; } workgroupBarrier(); if (tid < 16u) { red[tid] += red[tid + 16u]; } workgroupBarrier(); if (tid < 8u) { red[tid] += red[tid + 8u]; } workgroupBarrier(); if (tid < 4u) { red[tid] += red[tid + 4u]; } workgroupBarrier(); if (tid < 2u) { red[tid] += red[tid + 2u]; } workgroupBarrier(); if (tid < 1u) { red[tid] += red[tid + 1u]; } workgroupBarrier(); `; const REDUCE8MAX = ` if (tid < 128u) { red[tid] = max(red[tid], red[tid + 128u]); } workgroupBarrier(); if (tid < 64u) { red[tid] = max(red[tid], red[tid + 64u]); } workgroupBarrier(); if (tid < 32u) { red[tid] = max(red[tid], red[tid + 32u]); } workgroupBarrier(); if (tid < 16u) { red[tid] = max(red[tid], red[tid + 16u]); } workgroupBarrier(); if (tid < 8u) { red[tid] = max(red[tid], red[tid + 8u]); } workgroupBarrier(); if (tid < 4u) { red[tid] = max(red[tid], red[tid + 4u]); } workgroupBarrier(); if (tid < 2u) { red[tid] = max(red[tid], red[tid + 2u]); } workgroupBarrier(); if (tid < 1u) { red[tid] = max(red[tid], red[tid + 1u]); } workgroupBarrier(); `; const GEMM = ` enable chromium_experimental_subgroup_matrix; diagnostic(off, chromium.subgroup_matrix_uniformity); struct P { M: u32, N: u32, K: u32, pad: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var A: array; @group(0) @binding(2) var B: array; @group(0) @binding(3) var bias: array; @group(0) @binding(4) var C: array; const TR: u32 = 128u; const TC: u32 = 64u; const TK: u32 = 32u; var tile_A: array; var tile_B: array; var scratch: array, 8>; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let bm = wg.x * TR; let bn = wg.y * TC; let sg = tid / 32u; let lane = tid % 32u; let sg_row = sg / 2u; let sg_col = sg % 2u; var acc: array, 16>; for (var i = 0u; i < 16u; i++) { acc[i] = subgroup_matrix_result(); } let nk = min((p.K + TK - 1u) / TK, 2048u); for (var kt = 0u; kt < nk; kt++) { let k0 = kt * TK; for (var i = 0u; i < 16u; i++) { let idx = i * 256u + tid; let r = idx / TK; let c = idx % TK; let gr = bm + r; let gc = k0 + c; var v: f16 = f16(0.0); if (gr < p.M && gc < p.K) { v = A[gr * p.K + gc]; } tile_A[idx] = v; } for (var i = 0u; i < 8u; i++) { let idx = i * 256u + tid; let gn = idx % TC; let gk = idx / TC; let gr = bn + gn; let gc = k0 + gk; var v: f16 = f16(0.0); if (gr < p.N && gc < p.K) { v = B[gc * p.N + gr]; } tile_B[gn * TK + gk] = v; } workgroupBarrier(); for (var step = 0u; step < TK; step += 8u) { let a_off = (sg_row * 32u) * TK + step; let m0 = subgroupMatrixLoad>(&tile_A, a_off, false, TK); let m1 = subgroupMatrixLoad>(&tile_A, a_off + 8u * TK, false, TK); let m2 = subgroupMatrixLoad>(&tile_A, a_off + 16u * TK, false, TK); let m3 = subgroupMatrixLoad>(&tile_A, a_off + 24u * TK, false, TK); let b_off = (sg_col * 32u) * TK + step; let n0 = subgroupMatrixLoad>(&tile_B, b_off, true, TK); let n1 = subgroupMatrixLoad>(&tile_B, b_off + 8u * TK, true, TK); let n2 = subgroupMatrixLoad>(&tile_B, b_off + 16u * TK, true, TK); let n3 = subgroupMatrixLoad>(&tile_B, b_off + 24u * TK, true, TK); acc[0] = subgroupMatrixMultiplyAccumulate(m0, n0, acc[0]); acc[1] = subgroupMatrixMultiplyAccumulate(m0, n1, acc[1]); acc[2] = subgroupMatrixMultiplyAccumulate(m0, n2, acc[2]); acc[3] = subgroupMatrixMultiplyAccumulate(m0, n3, acc[3]); acc[4] = subgroupMatrixMultiplyAccumulate(m1, n0, acc[4]); acc[5] = subgroupMatrixMultiplyAccumulate(m1, n1, acc[5]); acc[6] = subgroupMatrixMultiplyAccumulate(m1, n2, acc[6]); acc[7] = subgroupMatrixMultiplyAccumulate(m1, n3, acc[7]); acc[8] = subgroupMatrixMultiplyAccumulate(m2, n0, acc[8]); acc[9] = subgroupMatrixMultiplyAccumulate(m2, n1, acc[9]); acc[10] = subgroupMatrixMultiplyAccumulate(m2, n2, acc[10]); acc[11] = subgroupMatrixMultiplyAccumulate(m2, n3, acc[11]); acc[12] = subgroupMatrixMultiplyAccumulate(m3, n0, acc[12]); acc[13] = subgroupMatrixMultiplyAccumulate(m3, n1, acc[13]); acc[14] = subgroupMatrixMultiplyAccumulate(m3, n2, acc[14]); acc[15] = subgroupMatrixMultiplyAccumulate(m3, n3, acc[15]); } workgroupBarrier(); } let row0 = bm + sg_row * 32u; let col0 = bn + sg_col * 32u; for (var f = 0u; f < 16u; f++) { subgroupMatrixStore(&scratch[sg], 0u, acc[f], false, 8u); workgroupBarrier(); let fr = f / 4u; let fc = f % 4u; for (var e = lane; e < 64u; e += 32u) { let i = e / 8u; let j = e % 8u; let gr = row0 + fr * 8u + i; let gc = col0 + fc * 8u + j; if (gr < p.M && gc < p.N) { C[gr * p.N + gc] = f16(clamp(scratch[sg][e] + f32(bias[gc]), -60000.0, 60000.0)); } } workgroupBarrier(); } } `; const GEMM_SIMT_I4 = ` struct P { M: u32, N: u32, K: u32, group: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var A: array; @group(0) @binding(2) var Pk: array; @group(0) @binding(3) var Sc: array; @group(0) @binding(4) var bias: array; @group(0) @binding(5) var C: array; const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u; var As: array; var Bs: array; @compute @workgroup_size(16, 16) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let bm = wg.x * BM; let bn = wg.y * BN; let ty = tid / 16u; let tx = tid % 16u; var acc: array, 4>; for (var i = 0u; i < 4u; i++) { acc[i] = vec4(0.0); } let nk = min((p.K + BK - 1u) / BK, 2048u); for (var kt = 0u; kt < nk; kt++) { let k0 = kt * BK; for (var i = 0u; i < 8u; i++) { let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK; let gr = bm + r; let gc = k0 + c; var v: f32 = 0.0; if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); } As[r * BK + c] = v; } // dequant B tile: 256 words (16 kp x 16 words of 4 columns) let kp = tid / 16u; let n0 = (tid % 16u) * 4u; let gk2 = k0 / 2u + kp; let gn0 = bn + n0; var word: u32 = 0u; if (gk2 < p.K / 2u && gn0 < p.N) { word = Pk[(gk2 * p.N + gn0) / 4u]; } let grp = (k0 + 2u * kp) / p.group; for (var j = 0u; j < 4u; j++) { let n = n0 + j; if (bn + n < p.N) { let byte = (word >> (8u * j)) & 0xFFu; let s = f32(Sc[grp * p.N + bn + n]); Bs[(2u * kp) * BN + n] = (f32(byte & 0xFu) - 8.0) * s; Bs[(2u * kp + 1u) * BN + n] = (f32(byte >> 4u) - 8.0) * s; } else { Bs[(2u * kp) * BN + n] = 0.0; Bs[(2u * kp + 1u) * BN + n] = 0.0; } } workgroupBarrier(); for (var k = 0u; k < BK; k++) { let a0 = As[(ty * 4u + 0u) * BK + k]; let a1 = As[(ty * 4u + 1u) * BK + k]; let a2 = As[(ty * 4u + 2u) * BK + k]; let a3 = As[(ty * 4u + 3u) * BK + k]; let b0 = Bs[k * BN + tx * 4u + 0u]; let b1 = Bs[k * BN + tx * 4u + 1u]; let b2 = Bs[k * BN + tx * 4u + 2u]; let b3 = Bs[k * BN + tx * 4u + 3u]; let bv = vec4(b0, b1, b2, b3); acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; } workgroupBarrier(); } for (var r = 0u; r < 4u; r++) { let gr = bm + ty * 4u + r; if (gr < p.M) { for (var c = 0u; c < 4u; c++) { let gc = bn + tx * 4u + c; if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); } } } } } `; const GEMM_SIMT = ` struct P { M: u32, N: u32, K: u32, pad: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var A: array; @group(0) @binding(2) var B: array; @group(0) @binding(3) var bias: array; @group(0) @binding(4) var C: array; const BM: u32 = 64u; const BN: u32 = 64u; const BK: u32 = 32u; var As: array; var Bs: array; @compute @workgroup_size(16, 16) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let bm = wg.x * BM; let bn = wg.y * BN; let ty = tid / 16u; let tx = tid % 16u; var acc: array, 4>; for (var i = 0u; i < 4u; i++) { acc[i] = vec4(0.0); } let nk = min((p.K + BK - 1u) / BK, 2048u); for (var kt = 0u; kt < nk; kt++) { let k0 = kt * BK; for (var i = 0u; i < 8u; i++) { let idx = i * 256u + tid; let r = idx / BK; let c = idx % BK; let gr = bm + r; let gc = k0 + c; var v: f32 = 0.0; if (gr < p.M && gc < p.K) { v = f32(A[gr * p.K + gc]); } As[r * BK + c] = v; } for (var i = 0u; i < 8u; i++) { let idx = i * 256u + tid; let r = idx / BN; let c = idx % BN; let gr = k0 + r; let gc = bn + c; var v: f32 = 0.0; if (gr < p.K && gc < p.N) { v = f32(B[gr * p.N + gc]); } Bs[r * BN + c] = v; } workgroupBarrier(); for (var k = 0u; k < BK; k++) { let a0 = As[(ty * 4u + 0u) * BK + k]; let a1 = As[(ty * 4u + 1u) * BK + k]; let a2 = As[(ty * 4u + 2u) * BK + k]; let a3 = As[(ty * 4u + 3u) * BK + k]; let b0 = Bs[k * BN + tx * 4u + 0u]; let b1 = Bs[k * BN + tx * 4u + 1u]; let b2 = Bs[k * BN + tx * 4u + 2u]; let b3 = Bs[k * BN + tx * 4u + 3u]; let bv = vec4(b0, b1, b2, b3); acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; } workgroupBarrier(); } for (var r = 0u; r < 4u; r++) { let gr = bm + ty * 4u + r; if (gr < p.M) { for (var c = 0u; c < 4u; c++) { let gc = bn + tx * 4u + c; if (gc < p.N) { C[gr * p.N + gc] = f16(clamp(acc[r][c] + f32(bias[gc]), -60000.0, 60000.0)); } } } } } `; const GEMV = ` struct P { N: u32, K: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var A: array; @group(0) @binding(2) var B: array; @group(0) @binding(3) var bias: array; @group(0) @binding(4) var C: array; var shA: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); } workgroupBarrier(); let base = wg.x * 1024u + tid * 4u; var acc = vec4(0.0); let KK = min(p.K, 65536u); for (var k = 0u; k < KK; k++) { let a = shA[k]; let o = k * p.N + base; acc += a * vec4(f32(B[o]), f32(B[o + 1u]), f32(B[o + 2u]), f32(B[o + 3u])); } for (var j = 0u; j < 4u; j++) { let c = base + j; if (c < p.N) { C[c] = f16(acc[j] + f32(bias[c])); } } } `; const GEMV_I4 = ` struct P { N: u32, K: u32, group: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var A: array; @group(0) @binding(2) var Pk: array; @group(0) @binding(3) var Sc: array; @group(0) @binding(4) var bias: array; @group(0) @binding(5) var C: array; var shA: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { for (var i = tid; i < p.K; i += 256u) { shA[i] = f32(A[i]); } workgroupBarrier(); let base = wg.x * 1024u + tid * 4u; var acc = vec4(0.0); let KK = min(p.K, 65536u); for (var k = 0u; k < KK; k++) { let a = shA[k]; let word = Pk[((k / 2u) * p.N + base) / 4u]; let sh = (k & 1u) * 4u; let s = vec4(f32(Sc[(k / p.group) * p.N + base]), f32(Sc[(k / p.group) * p.N + base + 1u]), f32(Sc[(k / p.group) * p.N + base + 2u]), f32(Sc[(k / p.group) * p.N + base + 3u])); let q = vec4(f32((word >> sh) & 0xFu), f32((word >> (8u + sh)) & 0xFu), f32((word >> (16u + sh)) & 0xFu), f32((word >> (24u + sh)) & 0xFu)); acc += a * (q - vec4(8.0)) * s; } let bb = vec4(f32(bias[base]), f32(bias[base + 1u]), f32(bias[base + 2u]), f32(bias[base + 3u])); for (var j = 0u; j < 4u; j++) { if (base + j < p.N) { C[base + j] = f16(acc[j] + bb[j]); } } } `; const LNM = ` struct P { M: u32, D: u32, offS: u32, offT: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var MOD: array; @group(0) @binding(3) var Y: array; var red: array; var stats: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let row = wg.x; let D = p.D; let off = row * D; var s = 0.0; for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); } red[tid] = s; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { stats[0] = red[0] / f32(D); } workgroupBarrier(); let mu = stats[0]; var vs = 0.0; for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; } red[tid] = vs; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + 1e-6); } workgroupBarrier(); let inv = stats[1]; for (var i = tid; i < D; i += 256u) { let sc = 1.0 + f32(MOD[p.offS + i]); let sh = f32(MOD[p.offT + i]); Y[off + i] = f16((f32(X[off + i]) - mu) * inv * sc + sh); } } `; const LNA = ` struct P { M: u32, D: u32, pad0: u32, pad1: u32, eps: f32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Wt: array; @group(0) @binding(3) var Bs: array; @group(0) @binding(4) var Y: array; var red: array; var stats: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let row = wg.x; let D = p.D; let off = row * D; var s = 0.0; for (var i = tid; i < D; i += 256u) { s += f32(X[off + i]); } red[tid] = s; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { stats[0] = red[0] / f32(D); } workgroupBarrier(); let mu = stats[0]; var vs = 0.0; for (var i = tid; i < D; i += 256u) { let d = f32(X[off + i]) - mu; vs += d * d; } red[tid] = vs; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { stats[1] = inverseSqrt(red[0] / f32(D) + p.eps); } workgroupBarrier(); let inv = stats[1]; for (var i = tid; i < D; i += 256u) { Y[off + i] = f16((f32(X[off + i]) - mu) * inv * f32(Wt[i]) + f32(Bs[i])); } } `; const ROPE = ` struct P { M: u32, N: u32, total: u32, pad: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var FREQ: array; @group(0) @binding(2) var X: array; const SC: f32 = 0.35355339059327379; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.total) { return; } let pair = i % 32u; let t = i / 32u; let h = t % 16u; let row = t / 16u; let pos = row % p.N; let theta = f32(pos) * FREQ[pair]; let c = cos(theta); let s = sin(theta); let base = row * 1024u + h * 64u + pair * 2u; let x0 = f32(X[base]); let x1 = f32(X[base + 1u]); X[base] = f16((x0 * c - x1 * s) * SC); X[base + 1u] = f16((x0 * s + x1 * c) * SC); } `; const QK = ` struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var Q: array; @group(0) @binding(2) var K: array; @group(0) @binding(3) var S: array; var Qs: array; var Ks: array; @compute @workgroup_size(16, 16) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let b = wg.z / 16u; let h = wg.z % 16u; let qi = wg.x * 64u; let kj = wg.y * 64u; let N = min(p.N, 8192u); for (var i = 0u; i < 16u; i++) { let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; let qr = qi + r; var v: f16 = f16(0.0); if (qr < N) { v = Q[((b * N + qr) * 16u + h) * 64u + c]; } Qs[idx] = v; } for (var i = 0u; i < 16u; i++) { let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; let kr = kj + r; var v: f16 = f16(0.0); if (kr < N) { v = K[((b * N + kr) * 16u + h) * 64u + c]; } Ks[idx] = v; } workgroupBarrier(); let ty = tid / 16u; let tx = tid % 16u; var acc: array, 4>; for (var i = 0u; i < 4u; i++) { acc[i] = vec4(0.0); } for (var k = 0u; k < 64u; k++) { let a0 = f32(Qs[(ty * 4u + 0u) * 64u + k]); let a1 = f32(Qs[(ty * 4u + 1u) * 64u + k]); let a2 = f32(Qs[(ty * 4u + 2u) * 64u + k]); let a3 = f32(Qs[(ty * 4u + 3u) * 64u + k]); let b0 = f32(Ks[(tx * 4u + 0u) * 64u + k]); let b1 = f32(Ks[(tx * 4u + 1u) * 64u + k]); let b2 = f32(Ks[(tx * 4u + 2u) * 64u + k]); let b3 = f32(Ks[(tx * 4u + 3u) * 64u + k]); let bv = vec4(b0, b1, b2, b3); acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; } let bidx = wg.z; for (var r = 0u; r < 4u; r++) { let gr = qi + ty * 4u + r; if (gr < N) { for (var c = 0u; c < 4u; c++) { let gc = kj + tx * 4u + c; if (gc < N) { S[(bidx * N + gr) * N + gc] = acc[r][c]; } } } } } `; const SOFTMAX = ` struct P { N: u32, rows: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var S: array; var red: array; var st: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let off = wg.x * p.N; var m = -3.4e38; for (var i = tid; i < p.N; i += 256u) { m = max(m, S[off + i]); } red[tid] = m; workgroupBarrier(); ${REDUCE8MAX} if (tid == 0u) { st[0] = red[0]; } workgroupBarrier(); let mx = st[0]; var sum = 0.0; for (var i = tid; i < p.N; i += 256u) { sum += exp(S[off + i] - mx); } red[tid] = sum; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { st[1] = 1.0 / red[0]; } workgroupBarrier(); let inv = st[1]; for (var i = tid; i < p.N; i += 256u) { S[off + i] = exp(S[off + i] - mx) * inv; } } `; const AV = ` struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var S: array; @group(0) @binding(2) var V: array; @group(0) @binding(3) var O: array; var Ss: array; var Vs: array; @compute @workgroup_size(16, 16) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let b = wg.z / 16u; let h = wg.z % 16u; let qi = wg.x * 64u; let N = min(p.N, 8192u); let bidx = wg.z; let ty = tid / 16u; let tx = tid % 16u; var acc: array, 4>; for (var i = 0u; i < 4u; i++) { acc[i] = vec4(0.0); } let nk = min((N + 63u) / 64u, 256u); for (var kt = 0u; kt < nk; kt++) { let k0 = kt * 64u; for (var i = 0u; i < 16u; i++) { let idx = i * 256u + tid; let r = idx / 64u; let c = idx % 64u; let gr = qi + r; let skey = k0 + c; let vkey = k0 + r; var v0: f32 = 0.0; if (gr < N && skey < N) { v0 = S[(bidx * N + gr) * N + skey]; } Ss[r * 64u + c] = v0; var v1: f16 = f16(0.0); if (vkey < N) { v1 = V[((b * N + vkey) * 16u + h) * 64u + c]; } Vs[r * 64u + c] = v1; } workgroupBarrier(); for (var k = 0u; k < 64u; k++) { let a0 = Ss[(ty * 4u + 0u) * 64u + k]; let a1 = Ss[(ty * 4u + 1u) * 64u + k]; let a2 = Ss[(ty * 4u + 2u) * 64u + k]; let a3 = Ss[(ty * 4u + 3u) * 64u + k]; let b0 = f32(Vs[k * 64u + tx * 4u + 0u]); let b1 = f32(Vs[k * 64u + tx * 4u + 1u]); let b2 = f32(Vs[k * 64u + tx * 4u + 2u]); let b3 = f32(Vs[k * 64u + tx * 4u + 3u]); let bv = vec4(b0, b1, b2, b3); acc[0] += a0 * bv; acc[1] += a1 * bv; acc[2] += a2 * bv; acc[3] += a3 * bv; } workgroupBarrier(); } for (var r = 0u; r < 4u; r++) { let gr = qi + ty * 4u + r; if (gr < N) { for (var c = 0u; c < 4u; c++) { O[((b * N + gr) * 16u + h) * 64u + tx * 4u + c] = f16(acc[r][c]); } } } } `; const ADDGATE = ` struct P { M: u32, D: u32, gOff: u32, pad: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Z: array; @group(0) @binding(3) var MOD: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.M * p.D) { return; } let d = i % p.D; X[i] = f16(f32(X[i]) + f32(MOD[p.gOff + d]) * f32(Z[i])); } `; const GELU = ` struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; fn erf(x: f32) -> f32 { let ax = abs(x); let t = 1.0 / (1.0 + 0.3275911 * ax); let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-ax * ax); return select(-y, y, x >= 0.0); } @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } let x = f32(X[gid.x]); X[gid.x] = f16(0.5 * x * (1.0 + erf(x * 0.70710678118654752))); } `; const SILU = ` struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } let x = f32(X[gid.x]); X[gid.x] = f16(x / (1.0 + exp(-x))); } `; const MISH = ` struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; fn tanh_safe(v: f32) -> f32 { if (v > 15.0) { return 1.0; } if (v < -15.0) { return -1.0; } return tanh(v); } @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } let x = f32(X[gid.x]); var sp = x; if (x < 20.0) { sp = log(1.0 + exp(x)); } X[gid.x] = f16(x * tanh_safe(sp)); } `; const CONCAT3 = ` struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var C: array; @group(0) @binding(3) var T: array; @group(0) @binding(4) var O: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.B * p.N * 712u) { return; } let col = i % 712u; let row = i / 712u; let b = row / p.N; let f = row % p.N; var v: f16 = f16(0.0); if (col < 100u) { v = X[f * 100u + col]; } else if (col < 200u) { if (b == 0u) { v = C[f * 100u + (col - 100u)]; } } else { if (b == 0u) { v = T[f * 512u + (col - 200u)]; } } O[i] = v; } `; const GATHER = ` struct P { N: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var IDS: array; @group(0) @binding(2) var EMB: array; @group(0) @binding(3) var FRQ: array; @group(0) @binding(4) var O: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.N * 512u) { return; } let f = i / 512u; let d = i % 512u; let id = IDS[f] + 1; if (id <= 0) { O[i] = f16(0.0); return; } O[i] = f16(f32(EMB[u32(id) * 512u + d]) + f32(FRQ[f * 512u + d])); } `; const DWCONV = ` struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Wt: array; @group(0) @binding(3) var Bs: array; @group(0) @binding(4) var Y: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.M * p.D) { return; } let d = i32(i % p.D); let f = i32(i / p.D); var acc = f32(Bs[u32(d)]); for (var k = 0; k < 7; k++) { let ff = f - 3 + k; if (ff >= 0 && ff < i32(p.M)) { acc += f32(Wt[u32(d) * 7u + u32(k)]) * f32(X[u32(ff) * p.D + u32(d)]); } } Y[i] = f16(acc); } `; const GRNRED = ` struct P { M: u32, D: u32, off: u32, pad: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var NM: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3) { let d = wg.x + p.off; var s = 0.0; for (var f = 0u; f < p.M; f++) { let v = f32(X[f * p.D + d]); s += v * v; } NM[d] = sqrt(s); } `; const GRNMEAN = ` struct P { D: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var NM: array; @group(0) @binding(2) var MEAN: array; var red: array; @compute @workgroup_size(256) fn main(@builtin(local_invocation_index) tid: u32) { var acc = 0.0; for (var c = tid; c < p.D; c += 256u) { acc += NM[c]; } red[tid] = acc; workgroupBarrier(); ${REDUCE8} if (tid == 0u) { MEAN[0] = red[0] / f32(p.D); } } `; const GRNAPPLY = ` struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Y: array; @group(0) @binding(3) var G: array; @group(0) @binding(4) var B: array; @group(0) @binding(5) var NM: array; @group(0) @binding(6) var MEAN: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.M * p.D) { return; } let d = i % p.D; let v = f32(X[i]); Y[i] = f16(v * (NM[d] / (MEAN[0] + 1e-6)) * f32(G[d]) + f32(B[d]) + v); } `; const ADDRES = ` struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var Y: array; @group(0) @binding(2) var X: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } Y[gid.x] = f16(f32(Y[gid.x]) + f32(X[gid.x])); } `; const ZEROFILL = ` struct P { M: u32, D: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var IDS: array; @group(0) @binding(2) var X: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.M * p.D) { return; } if (IDS[i / p.D] < 0) { X[i] = f16(0.0); } } `; const CONVPOS = ` struct P { N: u32, M: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Wt: array; @group(0) @binding(3) var Bs: array; @group(0) @binding(4) var Y: array; var Ws: array; var Xs: array; @compute @workgroup_size(256) fn main(@builtin(workgroup_id) wg: vec3, @builtin(local_invocation_index) tid: u32) { let g = wg.x / 16u; let pt = wg.x % 16u; let p0 = pt * 64u; let b = wg.y; let tx = tid % 16u; let ty = tid / 16u; let c0 = g * 64u + ty * 4u; var acc: array; for (var i = 0u; i < 16u; i++) { acc[i] = 0.0; } for (var cc = 0u; cc < 32u; cc++) { let ci0 = cc * 2u; for (var i = tid; i < 2u * 64u * 31u; i += 256u) { let k = i % 31u; let oc = (i / 31u) % 64u; let ic = i / (31u * 64u); Ws[i] = Wt[((g * 64u + oc) * 64u + (ci0 + ic)) * 31u + k]; } for (var i = tid; i < 2u * 94u; i += 256u) { let ic = i / 94u; let pp = i % 94u; let pos = i32(p0) - 15 + i32(pp); var v: f32 = 0.0; if (pos >= 0 && pos < i32(p.N)) { v = f32(X[(b * p.N + u32(pos)) * 1024u + g * 64u + ci0 + ic]); } Xs[i] = v; } workgroupBarrier(); for (var ic = 0u; ic < 2u; ic++) { for (var k = 0u; k < 31u; k++) { let x0 = Xs[ic * 94u + tx * 4u + k]; let x1 = Xs[ic * 94u + tx * 4u + 1u + k]; let x2 = Xs[ic * 94u + tx * 4u + 2u + k]; let x3 = Xs[ic * 94u + tx * 4u + 3u + k]; let w0 = f32(Ws[(ic * 64u + ty * 4u + 0u) * 31u + k]); let w1 = f32(Ws[(ic * 64u + ty * 4u + 1u) * 31u + k]); let w2 = f32(Ws[(ic * 64u + ty * 4u + 2u) * 31u + k]); let w3 = f32(Ws[(ic * 64u + ty * 4u + 3u) * 31u + k]); acc[0] += x0 * w0; acc[1] += x0 * w1; acc[2] += x0 * w2; acc[3] += x0 * w3; acc[4] += x1 * w0; acc[5] += x1 * w1; acc[6] += x1 * w2; acc[7] += x1 * w3; acc[8] += x2 * w0; acc[9] += x2 * w1; acc[10] += x2 * w2; acc[11] += x2 * w3; acc[12] += x3 * w0; acc[13] += x3 * w1; acc[14] += x3 * w2; acc[15] += x3 * w3; } } workgroupBarrier(); } for (var oc = 0u; oc < 4u; oc++) { for (var j = 0u; j < 4u; j++) { let pos = p0 + tx * 4u + j; if (pos < p.N) { Y[(b * p.N + pos) * 1024u + c0 + oc] = f16(clamp(acc[j * 4u + oc] + f32(Bs[c0 + oc]), -60000.0, 60000.0)); } } } } `; const FLOW = ` struct P { total: u32, N: u32, B: u32, pad1: u32, dt: f32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var V: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } let i = gid.x; let d = i % 100u; let f = i / 100u; let v0 = f32(V[f * 100u + d]); var v = v0; if (p.B == 2u) { let v1 = f32(V[(p.N + f) * 100u + d]); v = v0 + (v0 - v1) * 2.0; } X[i] = f16(f32(X[i]) + p.dt * v); } `; const TEB = ` struct P { N: u32, B: u32, pad0: u32, pad1: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var T: array; @group(0) @binding(2) var O: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { let i = gid.x; if (i >= p.B * p.N * 512u) { return; } let row = i / 512u; let d = i % 512u; if (row < p.N) { O[i] = T[row * 512u + d]; } else { O[i] = f16(0.0); } } `; const COPY = ` struct P { total: u32, pad0: u32, pad1: u32, pad2: u32 }; @group(0) @binding(0) var p: P; @group(0) @binding(1) var X: array; @group(0) @binding(2) var Y: array; @compute @workgroup_size(256) fn main(@builtin(global_invocation_id) gid: vec3) { if (gid.x >= p.total) { return; } Y[gid.x] = X[gid.x]; } `; export const KERNELS = { gemm: GEMM, gemv: GEMV, lnm: LNM, lna: LNA, rope: ROPE, qk: QK, softmax: SOFTMAX, av: AV, addgate: ADDGATE, gelu: GELU, silu: SILU, mish: MISH, concat3: CONCAT3, gather: GATHER, dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN, grnapply: GRNAPPLY, addres: ADDRES, zerofill: ZEROFILL, convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY, }; function f16tof32(h) { const sign = (h & 0x8000) ? -1 : 1, exp = (h >> 10) & 0x1f, frac = h & 0x3ff; if (exp === 0) return sign * Math.pow(2, -14) * (frac / 1024); if (exp === 31) return frac ? NaN : sign * Infinity; return sign * Math.pow(2, exp - 15) * (1 + frac / 1024); } export class F5Engine { static async create(device, opts = {}) { const e = new F5Engine(device, opts); await e._init(); return e; } constructor(device, opts) { this.device = device; this.opts = opts; this.f32 = opts.dtype === 'f32'; this.es = this.f32 ? 4 : 2; } async _init() { const dev = this.device; const [mr, br] = await Promise.all([ fetch(this.opts.metaUrl || '/webgpu/f5w.json'), fetch(this.opts.binUrl || '/webgpu/f5w.bin'), ]); this.meta = await mr.json(); this.binSize = Number(br.headers.get('content-length')) || 0; this.wsize = Math.ceil((this.f32 ? this.binSize * 2 : this.binSize) / 256) * 256; this._planChunks(); this.wbufs = this.chunkStarts.map((st, i) => { const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize; return dev.createBuffer({ size: Math.ceil((end - st) / 256) * 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }); }); await this._streamWeights(br); this.ubufs = []; this.ubNext = 0; this.compileErrors = []; const cre = (name, code) => { if (this.f32) code = code.replace(/enable f16;\n/g, '').replace(/\bf16\b/g, 'f32'); const mod = dev.createShaderModule({ code: (this.f32 ? '' : PRELUDE) + code }); mod.getCompilationInfo().then((ci) => { for (const m of ci.messages) if (m.type === 'error') this.compileErrors.push(name + ': ' + m.message + ' @' + m.lineNum); }); return dev.createComputePipelineAsync({ layout: 'auto', compute: { module: mod, entryPoint: 'main' } }); }; this.gemmKind = this.opts.gemm || (this.device.features.has('chromium-experimental-subgroup-matrix') ? 'sgmat' : 'simt'); console.log('DBG gemmKind', this.gemmKind, 'opts', JSON.stringify(Object.keys(this.opts || {}))); const defs = { gemm: this.gemmKind === 'sgmat' ? GEMM : GEMM_SIMT, gemm_i4: GEMM_SIMT_I4, gemv: GEMV, gemv_i4: GEMV_I4, lnm: LNM, lna: LNA, rope: ROPE, qk: QK, softmax: SOFTMAX, av: AV, addgate: ADDGATE, gelu: GELU, silu: SILU, mish: MISH, concat3: CONCAT3, gather: GATHER, dwconv: DWCONV, grnred: GRNRED, grnmean: GRNMEAN, grnapply: GRNAPPLY, addres: ADDRES, zerofill: ZEROFILL, convpos: CONVPOS, flow: FLOW, teb: TEB, copy: COPY, }; this.pipe = {}; for (const [name, code] of Object.entries(defs)) { this.pipe[name] = await cre(name, code); } this.pipeNames = new Map(Object.entries(this.pipe).map(([k, v]) => [v, k])); } _planChunks() { const mult = this.f32 ? 2 : 1; const ents = Object.entries(this.meta).map(([n, t]) => { if (t.q) return { n, off: t.packed * mult, len: (t.scales + t.scale_bytes) - t.packed }; const len = Math.ceil(t.shape.reduce((a, b) => a * b, 1) * this.es / 4) * 4; return { n, off: t.offset * mult, len }; }).sort((a, b) => a.off - b.off); const CHUNK = 640 << 20; const starts = [0]; let cur = 0; this.bufOf = new Map(); for (const e of ents) { if (cur > 0 && cur + e.len > CHUNK) { starts.push(e.off); cur = 0; } this.bufOf.set(e.n, starts.length - 1); cur = e.off + e.len - starts[starts.length - 1]; } this.chunkStarts = starts; } _writeAt(globalOff, data, byteLen) { let off = globalOff, src = 0, remain = byteLen; while (remain > 0) { let bi = this.chunkStarts.length - 1; for (let i = 0; i < this.chunkStarts.length; i++) { const end = i + 1 < this.chunkStarts.length ? this.chunkStarts[i + 1] : this.wsize; if (off < end) { bi = i; break; } } const base = this.chunkStarts[bi]; const bo = off - base; const end = bi + 1 < this.chunkStarts.length ? this.chunkStarts[bi + 1] : this.wsize; const n = Math.min(remain, end - off); if (data instanceof Float32Array) { this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src / 4, (src + n) / 4)); } else { this.device.queue.writeBuffer(this.wbufs[bi], bo, data.subarray(src, src + n)); } off += n; src += n; remain -= n; } } async _streamWeights(res) { // Stream the bundle to the GPU in ~32 MB slices; convert f16 -> f32 if needed. const dev = this.device; const reader = res.body.getReader(); const CH = 32 << 20; let stage = new Uint8Array(CH + 8); let staged = 0, gpuOff = 0; const flush = async (final) => { if (!staged) return; let n = staged; const extra = final ? (4 - (n % 4)) % 4 : 0; if (extra) { stage.fill(0, n, n + extra); n += extra; } if (this.f32) { const elems = n >> 1; const out = new Float32Array(elems); out.set(new Float16Array(stage.buffer, 0, elems)); this._writeAt(gpuOff, out, elems * 4); gpuOff += elems * 4; } else { this._writeAt(gpuOff, stage.subarray(0, n), n); gpuOff += n; } staged = 0; if (this.opts.onProgress) this.opts.onProgress(gpuOff / (this.f32 ? this.binSize * 2 : this.binSize)); }; while (true) { const { done, value } = await reader.read(); if (done) break; let off = 0; while (off < value.length) { const take = Math.min(value.length - off, stage.length - staged); stage.set(value.subarray(off, off + take), staged); staged += take; off += take; if (staged >= CH) await flush(false); } } await flush(true); const pad = Math.ceil(gpuOff / 256) * 256 - gpuOff; if (pad > 0) this._writeAt(gpuOff, new Uint8Array(pad), pad); } _t(name) { const t = this.meta[name]; if (!t) throw new Error('missing ' + name); return t; } _wslice(name) { const t = this._t(name); const n = t.shape.reduce((a, b) => a * b, 1); const mult = this.f32 ? 2 : 1; const bi = this.bufOf.get(name); const base = this.chunkStarts[bi]; return { buffer: this.wbufs[bi], offset: t.offset * mult - base, size: Math.ceil(n * this.es / 4) * 4 }; } _alc(bytes) { const size = Math.ceil(bytes / 256) * 256; return this.device.createBuffer({ size, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC }); } _ub(vals, floats) { const ab = new ArrayBuffer(256); const dv = new DataView(ab); for (let i = 0; i < vals.length; i++) { if (floats && floats.indexOf(i) >= 0) dv.setFloat32(i * 4, vals[i], true); else dv.setUint32(i * 4, vals[i] >>> 0, true); } const buf = this.device.createBuffer({ size: 256, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); this.device.queue.writeBuffer(buf, 0, ab); this.ubNext++; this.ubufs.push(buf); return { buffer: buf, offset: 0, size: 256 }; } _bg(pipe, u, bufs) { const wrap = (b) => (b && b.buffer) ? b : { buffer: b, offset: 0, size: b.size }; return this.device.createBindGroup({ layout: pipe.getBindGroupLayout(0), entries: [{ binding: 0, resource: wrap(u) }, ...bufs.map((b, i) => ({ binding: i + 1, resource: wrap(b) }))], }); } setup(N) { const dev = this.device; this.N = N; this.B = this.opts.branches || 2; this.M2 = this.B * N; const A = (name, elems) => (this[name] = this._alc(elems * this.es)); A('xBuf', N * 100); A('condBuf', N * 100); A('teBuf', N * 512); A('tebBuf', 2 * N * 512); A('catBuf', 2 * N * 712); A('hBuf', 2 * N * 1024); A('tmpBuf', 2 * N * 1024); A('qBuf', 2 * N * 1024); A('kBuf', 2 * N * 1024); A('vBuf', 2 * N * 1024); A('attnBuf', 2 * N * 1024); A('ffBuf', 2 * N * 2048); A('velBuf', 2 * N * 100); A('timeIn', 256); A('tembBuf', 1024); A('temb2Buf', 1024); A('modBuf', 137216); this.scoresBuf = this._alc(this.B * 16 * N * N * 4); A('tBuf', N * 1024); A('tBuf2', N * 1024); A('tBuf3', N * 2048); A('tnmBuf', 1024 * 4); A('tmeanBuf', 4); this.idsBuf = this._alc(N * 4); this.ubNext = 0; this.ropeFreq = dev.createBuffer({ size: 256, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }); const f = new Float32Array(64); for (let j = 0; j < 32; j++) f[j] = Math.pow(10000, -j / 32); dev.queue.writeBuffer(this.ropeFreq, 0, f); this.calls = []; this._buildStepCalls(); const stepCalls = this.calls; this.calls = []; this._buildTextCalls(); this.textCalls = this.calls; this.calls = stepCalls; } _add(pipe, bg, x, y = 1, z = 1, meta) { const c = { pipe, bg, x: Math.ceil(x), y, z, name: this.pipeNames.get(pipe), meta }; this.calls.push(c); if (this.debug) this.debug.push(c); return c; } _qdesc(name) { const t = this._t(name); const mult = this.f32 ? 2 : 1; const bi = this.bufOf.get(name); const base = this.chunkStarts[bi]; return { packed: { buffer: this.wbufs[bi], offset: t.packed * mult - base, size: t.packed_bytes }, scales: { buffer: this.wbufs[bi], offset: t.scales * mult - base, size: t.scale_bytes }, group: t.group, }; } _gemm(A, wn, bn, C, M, K, N) { const t = this._t(wn); if (t.q) { if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`); const q = this._qdesc(wn); if (q.packed.offset + q.packed.size > q.packed.buffer.size) throw new Error(`q4 packed OOB ${wn}`); const u = this._ub([M, N, K, q.group]); this._add(this.pipe.gemm_i4, this._bg(this.pipe.gemm_i4, u, [A, q.packed, q.scales, this._wslice(bn), C]), Math.ceil(M / 64), Math.ceil(N / 64)); return; } const wa = this._wslice(wn), ba = this._wslice(bn); if (!(M > 0 && N > 0 && K > 0 && M < 1e5 && N < 1e6 && K < 1e6)) throw new Error(`bad gemm args ${M},${K},${N} (${wn})`); if (wa.offset + wa.size > wa.buffer.size) throw new Error(`gemm weight OOB ${wn}`); if (ba.offset + ba.size > ba.buffer.size) throw new Error(`gemm bias OOB ${bn}`); const u = this._ub([M, N, K, 0]); 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, { vals: [M, N, K], wa: [wa.offset, wa.size, wa.buffer.size], ba: [ba.offset, ba.size, ba.buffer.size] }); } _gemv(A, wn, bn, C, K, N) { const t = this._t(wn); if (t.q) { const q = this._qdesc(wn); const u = this._ub([N, K, q.group, 0]); 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)); return; } const u = this._ub([N, K, 0, 0]); this._add(this.pipe.gemv, this._bg(this.pipe.gemv, u, [A, this._wslice(wn), this._wslice(bn), C]), Math.ceil(N / 1024)); } _elem(pipe, X, total) { const u = this._ub([total, 0, 0, 0]); this._add(pipe, this._bg(pipe, u, [X]), Math.ceil(total / 256)); } _buildStepCalls() { const N = this.N, M2 = this.M2; this._gemm(this.timeIn, 'time.0', 'time.0.b', this.tembBuf, 1, 256, 1024); this._elem(this.pipe.silu, this.tembBuf, 1024); this._gemm(this.tembBuf, 'time.2', 'time.2.b', this.temb2Buf, 1, 1024, 1024); this._elem(this.pipe.silu, this.temb2Buf, 1024); this._gemv(this.temb2Buf, 'ada.w', 'ada.b', this.modBuf, 1024, 137216); 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)); this._gemm(this.catBuf, 'in.proj', 'in.proj.b', this.hBuf, M2, 712, 1024); const cp = (X, wn, bn, Y) => { const u = this._ub([N, M2, 0, 0]); this._add(this.pipe.convpos, this._bg(this.pipe.convpos, u, [X, this._wslice(wn), this._wslice(bn), Y]), 16 * 16, this.B); }; cp(this.hBuf, 'convpos.0.w', 'convpos.0.b', this.tmpBuf); this._elem(this.pipe.mish, this.tmpBuf, M2 * 1024); cp(this.tmpBuf, 'convpos.2.w', 'convpos.2.b', this.qBuf); this._elem(this.pipe.mish, this.qBuf, M2 * 1024); { const u = this._ub([M2 * 1024, 0, 0, 0]); this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.hBuf, this.qBuf]), Math.ceil(M2 * 1024 / 256)); } for (let L = 0; L < 22; L++) { const base = L * 6144; { const u = this._ub([M2, 1024, base + 1024, base]); this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); } this._gemm(this.tmpBuf, `b${L}.q`, `b${L}.q.b`, this.qBuf, M2, 1024, 1024); this._gemm(this.tmpBuf, `b${L}.k`, `b${L}.k.b`, this.kBuf, M2, 1024, 1024); this._gemm(this.tmpBuf, `b${L}.v`, `b${L}.v.b`, this.vBuf, M2, 1024, 1024); for (const buf of [this.qBuf, this.kBuf]) { const u = this._ub([M2, N, M2 * 16 * 32, 0]); this._add(this.pipe.rope, this._bg(this.pipe.rope, u, [this.ropeFreq, buf]), Math.ceil(M2 * 16 * 32 / 256)); } { const u = this._ub([N, 0, 0, 0]); 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); const u2 = this._ub([N, this.B * 16 * N, 0, 0]); this._add(this.pipe.softmax, this._bg(this.pipe.softmax, u2, [this.scoresBuf]), this.B * 16 * N); const u3 = this._ub([N, 0, 0, 0]); 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); } this._gemm(this.attnBuf, `b${L}.o`, `b${L}.o.b`, this.tmpBuf, M2, 1024, 1024); { const u = this._ub([M2, 1024, base + 2048, 0]); this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256)); } { const u = this._ub([M2, 1024, base + 4096, base + 3072]); this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); } this._gemm(this.tmpBuf, `b${L}.up`, `b${L}.up.b`, this.ffBuf, M2, 1024, 2048); this._elem(this.pipe.gelu, this.ffBuf, M2 * 2048); this._gemm(this.ffBuf, `b${L}.down`, `b${L}.down.b`, this.tmpBuf, M2, 2048, 1024); { const u = this._ub([M2, 1024, base + 5120, 0]); this._add(this.pipe.addgate, this._bg(this.pipe.addgate, u, [this.hBuf, this.tmpBuf, this.modBuf]), Math.ceil(M2 * 1024 / 256)); } } const fbase = 22 * 6144; { const u = this._ub([M2, 1024, fbase, fbase + 1024]); this._add(this.pipe.lnm, this._bg(this.pipe.lnm, u, [this.hBuf, this.modBuf, this.tmpBuf]), M2); } this._gemm(this.tmpBuf, 'out.w', 'out.b', this.velBuf, M2, 1024, 100); this.flowUniform = this._ub([N * 100, N, this.B, 0]); this._add(this.pipe.flow, this._bg(this.pipe.flow, this.flowUniform, [this.xBuf, this.velBuf]), Math.ceil(N * 100 / 256)); } _buildTextCalls() { const N = this.N; { const u = this._ub([N, 0, 0, 0]); 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)); } for (let b = 0; b < 4; b++) { { const u = this._ub([N, 512, 0, 0]); 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)); } { const u = this._ub([N, 512, 0, 0, 1e-5, 0, 0, 0], [4]); 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); } this._gemm(this.tBuf3, `tb${b}.p1`, `tb${b}.p1.b`, this.tBuf2, N, 512, 1024); this._elem(this.pipe.gelu, this.tBuf2, N * 1024); { const u = this._ub([N, 1024, 0, 0]); this._add(this.pipe.grnred, this._bg(this.pipe.grnred, u, [this.tBuf2, this.tnmBuf]), 1024); const u2 = this._ub([1024, 0, 0, 0]); this._add(this.pipe.grnmean, this._bg(this.pipe.grnmean, u2, [this.tnmBuf, this.tmeanBuf]), 1); const u3 = this._ub([N, 1024, 0, 0]); 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)); } this._gemm(this.tBuf, `tb${b}.p2`, `tb${b}.p2.b`, this.tBuf2, N, 1024, 512); { const u = this._ub([N * 512, 0, 0, 0]); this._add(this.pipe.addres, this._bg(this.pipe.addres, u, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256)); const u2 = this._ub([N, 512, 0, 0]); this._add(this.pipe.zerofill, this._bg(this.pipe.zerofill, u2, [this.idsBuf, this.tBuf2]), Math.ceil(N * 512 / 256)); const u3 = this._ub([N * 512, 0, 0, 0]); this._add(this.pipe.copy, this._bg(this.pipe.copy, u3, [this.tBuf2, this.teBuf]), Math.ceil(N * 512 / 256)); } } { const u = this._ub([N, this.B, 0, 0]); this._add(this.pipe.teb, this._bg(this.pipe.teb, u, [this.teBuf, this.tebBuf]), Math.ceil(this.B * N * 512 / 256)); } } textNames() { return this.textCalls.map((c) => c.name); } async testGemm2(M, K, N, mode) { const dev = this.device; const mk = (bytes) => dev.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC }); const A = mode === 'dedicated' ? mk(M * K * 2) : this.tBuf3; const C = mode === 'dedicated' ? mk(M * N * 2) : this.tBuf2; const W = mode === 'dedicated' ? { buffer: mk(K * N * 2), offset: 0, size: K * N * 2 } : this._wslice('tb0.p1'); const Bs = mode === 'dedicated' ? { buffer: mk(N * 2), offset: 0, size: N * 2 } : this._wslice('tb0.p1.b'); const u = this._ub([M, N, K, 0]); const bg = this._bg(this.pipe.gemm, u, [A, W, Bs, C]); this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 128), y: Math.ceil(N / 64), z: 1 }]); await this.device.queue.onSubmittedWorkDone(); return mode + ' ok'; } async testGemm(M, K, N) { const u = this._ub([M, N, K, 0]); const bg = this._bg(this.pipe.gemm, u, [this.tBuf3, this._wslice('tb0.p1'), this._wslice('tb0.p1.b'), this.tBuf2]); this.encodeCalls([{ pipe: this.pipe.gemm, bg, x: Math.ceil(M / 64), y: Math.ceil(N / 64), z: 1 }]); await this.device.queue.onSubmittedWorkDone(); return M + 'x' + K + 'x' + N; } debugText() { return this.textCalls.map((c) => ({ name: c.name, x: c.x, y: c.y, meta: c.meta })); } async runTextPrefix(k) { this.encodeCalls(this.textCalls.slice(0, k)); await this.device.queue.onSubmittedWorkDone(); return k; } async runOne(list, k) { this.encodeCalls([list[k]]); await this.device.queue.onSubmittedWorkDone(); return list[k].name; } async setCond(condMelF32, ids, limit) { const dev = this.device, N = this.N; const ca = this.f32 ? new Float32Array(N * 100) : new Float16Array(N * 100); ca.set(condMelF32.subarray(0, N * 100)); dev.queue.writeBuffer(this.condBuf, 0, ca); const idarr = new Int32Array(N).fill(-1); for (let i = 0; i < Math.min(ids.length, N); i++) idarr[i] = ids[i]; dev.queue.writeBuffer(this.idsBuf, 0, idarr); const list = (limit === undefined) ? this.textCalls : this.textCalls.slice(0, limit); if (list.length) this.encodeCalls(list); if (limit !== 0) await dev.queue.onSubmittedWorkDone(); } setX(xF32) { const f = this.f32 ? new Float32Array(this.N * 100) : new Float16Array(this.N * 100); f.set(xF32.subarray(0, this.N * 100)); this.device.queue.writeBuffer(this.xBuf, 0, f); } encodeCalls(list) { const enc = this.device.createCommandEncoder(); const p = enc.beginComputePass(); for (const c of list) { p.setPipeline(c.pipe); p.setBindGroup(0, c.bg); p.dispatchWorkgroups(c.x, c.y, c.z); } p.end(); this.device.queue.submit([enc.finish()]); } writeTime(t) { const half = 128; const tb = this.f32 ? new Float32Array(256) : new Float16Array(256); for (let j = 0; j < 128; j++) { const fr = Math.exp(-Math.log(10000) / (half - 1) * j); const a = t * 1000 * fr; tb[j] = Math.sin(a); tb[128 + j] = Math.cos(a); } this.device.queue.writeBuffer(this.timeIn, 0, tb); } async step(t, dt) { this.writeTime(t); this.device.queue.writeBuffer(this.flowUniform.buffer, this.flowUniform.offset + 16, new Float32Array([dt])); this.encodeCalls(this.calls); await this.device.queue.onSubmittedWorkDone(); } async runStepPrefix(k, t) { this.writeTime(t === undefined ? 0.5 : t); this.encodeCalls(this.calls.slice(0, k)); await this.device.queue.onSubmittedWorkDone(); } async readBuf(buf, elems) { const bytes = elems * this.es; const staging = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); const enc = this.device.createCommandEncoder(); enc.copyBufferToBuffer(buf, 0, staging, 0, bytes); this.device.queue.submit([enc.finish()]); await staging.mapAsync(GPUMapMode.READ); const ab = staging.getMappedRange().slice(0); staging.unmap(); return this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); } async readVel() { const bytes = this.M2 * 100 * this.es; const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); const enc = this.device.createCommandEncoder(); enc.copyBufferToBuffer(this.velBuf, 0, buf, 0, bytes); this.device.queue.submit([enc.finish()]); await buf.mapAsync(GPUMapMode.READ); const ab = buf.getMappedRange().slice(0); const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); buf.unmap(); return out; } async readX() { const bytes = this.N * 100 * this.es; const buf = this.device.createBuffer({ size: Math.ceil(bytes / 4) * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST }); const enc = this.device.createCommandEncoder(); enc.copyBufferToBuffer(this.xBuf, 0, buf, 0, bytes); this.device.queue.submit([enc.finish()]); await buf.mapAsync(GPUMapMode.READ); const ab = buf.getMappedRange().slice(0); const out = this.f32 ? new Float32Array(ab) : new Float32Array(new Float16Array(ab)); buf.unmap(); return out; } }