GCStream commited on
Commit
db4a2ce
·
1 Parent(s): 7133356

Custom WGSL DiT engine (int4, CFG-free 4-step)

Browse files

Add 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

Files changed (3) hide show
  1. engine/f5engine.js +1324 -0
  2. tts.worker.js +73 -13
  3. 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
- const mel = await flowSample(
536
- (x, time) => ditStep(x, time, voice, ids, textLen, frames, refFrames),
537
- frames,
538
- MEL_BINS,
539
- {
540
- steps,
541
- sway: manifest.sampler?.sway_sampling_coef ?? -1,
542
- seed: message.seed ?? 1234,
543
- onStep: (step, total) => progress('synth', { step, steps: total, message: `flow step ${step}/${total}` }),
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"