volvoxai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +145 -0
- package/bin/volvox.js +72 -0
- package/dist/v0.1.0/volvoxai.js +4664 -0
- package/dist/v0.1.0/volvoxai.min.js +1848 -0
- package/dist/v0.1.0/volvoxai.wasm +0 -0
- package/dist/volvoxai.js +4664 -0
- package/dist/volvoxai.min.js +1848 -0
- package/dist/volvoxai.wasm +0 -0
- package/docs/README.md +22 -0
- package/docs/browser-runtime.md +87 -0
- package/docs/efficientdet_tflite_vs_volvoxai.md +445 -0
- package/docs/microkernel_optimization_guide.md +153 -0
- package/docs/model-format.md +108 -0
- package/docs/models.md +103 -0
- package/docs/native-runtime.md +189 -0
- package/docs/operation_list.md +232 -0
- package/docs/operator_fusion_patterns.md +58 -0
- package/docs/quickstart.md +115 -0
- package/docs/roadmap.md +19 -0
- package/docs/testing.md +97 -0
- package/docs/textbook/01-foundations.md +233 -0
- package/docs/textbook/02-tinystories-language-model.md +300 -0
- package/docs/textbook/03-efficientdet-vision-model.md +281 -0
- package/docs/textbook/04-precision-and-quantization.md +208 -0
- package/docs/textbook/05-inside-the-engine.md +155 -0
- package/docs/textbook/06-native-engine-architecture.md +338 -0
- package/docs/textbook/07-glossary-and-next-steps.md +258 -0
- package/docs/textbook/README.md +85 -0
- package/docs/textbook/ko/01-foundations.md +231 -0
- package/docs/textbook/ko/02-tinystories-language-model.md +300 -0
- package/docs/textbook/ko/03-efficientdet-vision-model.md +277 -0
- package/docs/textbook/ko/04-precision-and-quantization.md +206 -0
- package/docs/textbook/ko/05-inside-the-engine.md +154 -0
- package/docs/textbook/ko/06-native-engine-architecture.md +333 -0
- package/docs/textbook/ko/07-glossary-and-next-steps.md +253 -0
- package/docs/textbook/ko/README.md +83 -0
- package/docs/xnnpack_optimization_guide.md +197 -0
- package/js/CPUEngine.js +241 -0
- package/js/Graph.js +49 -0
- package/js/GraphExecutor.js +1020 -0
- package/js/GraphLoader.js +282 -0
- package/js/ShaderLibrary.js +236 -0
- package/js/Tensor.js +25 -0
- package/js/Tokenizer.js +266 -0
- package/js/VolvoxAI.js +130 -0
- package/js/WasmEngine.js +378 -0
- package/js/WebNNEngine.js +169 -0
- package/js/index.js +11 -0
- package/js/ops/add.js +31 -0
- package/js/ops/argMax.js +33 -0
- package/js/ops/averagePool2D.js +38 -0
- package/js/ops/batchNorm2D.js +28 -0
- package/js/ops/cast.js +19 -0
- package/js/ops/clip.js +15 -0
- package/js/ops/concat2.js +18 -0
- package/js/ops/conv1D.js +35 -0
- package/js/ops/conv2D.js +70 -0
- package/js/ops/convTranspose2D.js +45 -0
- package/js/ops/crossAttention.js +69 -0
- package/js/ops/crossSDPA.js +41 -0
- package/js/ops/dequantizeLinear.js +9 -0
- package/js/ops/div.js +15 -0
- package/js/ops/embedding.js +14 -0
- package/js/ops/expand.js +24 -0
- package/js/ops/gELU.js +9 -0
- package/js/ops/gather.js +51 -0
- package/js/ops/gatherElements.js +33 -0
- package/js/ops/globalAveragePool.js +21 -0
- package/js/ops/hardSigmoid.js +12 -0
- package/js/ops/hardSwish.js +12 -0
- package/js/ops/interp1D.js +25 -0
- package/js/ops/layerNorm.js +25 -0
- package/js/ops/leakyReLU.js +10 -0
- package/js/ops/logSoftmax.js +15 -0
- package/js/ops/matMul.js +35 -0
- package/js/ops/maxPool2D.js +36 -0
- package/js/ops/meanHeight.js +17 -0
- package/js/ops/mul.js +31 -0
- package/js/ops/nonMaxSuppression.js +72 -0
- package/js/ops/pReLU.js +11 -0
- package/js/ops/pad.js +35 -0
- package/js/ops/profileX.js +22 -0
- package/js/ops/profileY.js +22 -0
- package/js/ops/rMSNorm.js +14 -0
- package/js/ops/reLU.js +8 -0
- package/js/ops/reduceMean.js +17 -0
- package/js/ops/reduceSum.js +19 -0
- package/js/ops/reshape.js +6 -0
- package/js/ops/resize.js +44 -0
- package/js/ops/sDPA.js +44 -0
- package/js/ops/siLU.js +8 -0
- package/js/ops/sigmoid.js +6 -0
- package/js/ops/slice.js +36 -0
- package/js/ops/softmax.js +18 -0
- package/js/ops/spatialSoftargmaxY.js +28 -0
- package/js/ops/split.js +24 -0
- package/js/ops/sub.js +11 -0
- package/js/ops/tanh.js +7 -0
- package/js/ops/transpose.js +34 -0
- package/js/ops/upsample2x.js +23 -0
- package/js/ops/where.js +15 -0
- package/package.json +33 -0
- package/shaders/add.wgsl +13 -0
- package/shaders/add3Relu.wgsl +23 -0
- package/shaders/addRelu.wgsl +22 -0
- package/shaders/averagePool2D.wgsl +24 -0
- package/shaders/batchNorm2D.wgsl +21 -0
- package/shaders/binaryBroadcast.wgsl +34 -0
- package/shaders/broadcastBinary.wgsl +26 -0
- package/shaders/clip.wgsl +10 -0
- package/shaders/concat2.wgsl +16 -0
- package/shaders/concatCopy.wgsl +10 -0
- package/shaders/concatSigmoidCopy.wgsl +16 -0
- package/shaders/conv1D.wgsl +37 -0
- package/shaders/conv2D.wgsl +80 -0
- package/shaders/conv2DDepthwise4.wgsl +74 -0
- package/shaders/conv2DDepthwise8.wgsl +66 -0
- package/shaders/conv2DPointwise16.wgsl +67 -0
- package/shaders/conv2DPointwise16Tile.wgsl +86 -0
- package/shaders/conv2DPointwise8.wgsl +85 -0
- package/shaders/conv2DPointwise8Vec2.wgsl +70 -0
- package/shaders/conv2DPointwise8Vec4.wgsl +65 -0
- package/shaders/conv2DRegularC3Out16.wgsl +75 -0
- package/shaders/convTranspose2D.wgsl +33 -0
- package/shaders/copy.wgsl +13 -0
- package/shaders/crossAttention.wgsl +140 -0
- package/shaders/crossAttentionF32.wgsl +98 -0
- package/shaders/crossSDPA.wgsl +74 -0
- package/shaders/dequantizeLinear.wgsl +14 -0
- package/shaders/div.wgsl +34 -0
- package/shaders/elementwise.wgsl +13 -0
- package/shaders/embedding.wgsl +22 -0
- package/shaders/expand.wgsl +18 -0
- package/shaders/gELU.wgsl +13 -0
- package/shaders/gather.wgsl +17 -0
- package/shaders/generalTranspose.wgsl +19 -0
- package/shaders/globalAveragePool.wgsl +19 -0
- package/shaders/hardSigmoid.wgsl +13 -0
- package/shaders/hardSwish.wgsl +13 -0
- package/shaders/interp1D.wgsl +28 -0
- package/shaders/layerNorm.wgsl +33 -0
- package/shaders/leakyReLU.wgsl +11 -0
- package/shaders/linearF32.wgsl +33 -0
- package/shaders/linearF32RowMajor.wgsl +24 -0
- package/shaders/linearInt8.wgsl +42 -0
- package/shaders/logSoftmax.wgsl +22 -0
- package/shaders/maxPool2D.wgsl +37 -0
- package/shaders/meanHeight.wgsl +18 -0
- package/shaders/mul.wgsl +32 -0
- package/shaders/nonMaxSuppression.wgsl +92 -0
- package/shaders/pReLU.wgsl +14 -0
- package/shaders/pad.wgsl +19 -0
- package/shaders/profileX.wgsl +28 -0
- package/shaders/profileY.wgsl +28 -0
- package/shaders/quantizeLinear.wgsl +69 -0
- package/shaders/rMSNorm.wgsl +21 -0
- package/shaders/reLU.wgsl +13 -0
- package/shaders/reduce.wgsl +17 -0
- package/shaders/resize.wgsl +52 -0
- package/shaders/sDPA.wgsl +71 -0
- package/shaders/siLU.wgsl +13 -0
- package/shaders/sigmoid.wgsl +13 -0
- package/shaders/slice.wgsl +26 -0
- package/shaders/softmax.wgsl +23 -0
- package/shaders/spatialSoftargmaxY.wgsl +32 -0
- package/shaders/split.wgsl +15 -0
- package/shaders/sub.wgsl +34 -0
- package/shaders/tanh.wgsl +13 -0
- package/shaders/upsample2x.wgsl +24 -0
- package/shaders/where.wgsl +12 -0
- package/volvoxai.wasm +0 -0
|
@@ -0,0 +1,4664 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res, err) => function __init() {
|
|
4
|
+
if (err) throw err[0];
|
|
5
|
+
try {
|
|
6
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
throw err = [e], e;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// shaders/linearF32.wgsl
|
|
17
|
+
var linearF32_default;
|
|
18
|
+
var init_linearF32 = __esm({
|
|
19
|
+
"shaders/linearF32.wgsl"() {
|
|
20
|
+
linearF32_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight_f32 : array<f32>;\n @group(0) @binding(2) var<storage, read> dummyScale : array<f32>;\n @group(0) @binding(3) var<storage, read> bias : array<f32>;\n @group(0) @binding(4) var<storage, read_write> output : array<f32>;\n\n struct Params {\n seq_len : u32,\n d_in : u32,\n d_out : u32,\n }\n @group(0) @binding(5) var<uniform> params : Params;\n\n @compute @workgroup_size(64, 1, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let row = global_id.y;\n let col = global_id.x;\n \n if (row >= params.seq_len || col >= params.d_out) { return; }\n \n var sum : f32 = 0.0;\n \n for (var k = 0u; k < params.d_in; k = k + 1u) {\n let in_val = input[row * params.d_in + k];\n let w_val = weight_f32[col * params.d_in + k];\n sum = sum + in_val * w_val;\n }\n \n let b_val = bias[col];\n // dummyScale is passed just to keep bindings consistent but not used here.\n \n output[row * params.d_out + col] = sum + b_val;\n }\n";
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// shaders/linearInt8.wgsl
|
|
25
|
+
var linearInt8_default;
|
|
26
|
+
var init_linearInt8 = __esm({
|
|
27
|
+
"shaders/linearInt8.wgsl"() {
|
|
28
|
+
linearInt8_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight_int8_packed : array<u32>;\n @group(0) @binding(2) var<storage, read> weight_scales : array<f32>;\n @group(0) @binding(3) var<storage, read> bias : array<f32>;\n @group(0) @binding(4) var<storage, read_write> output : array<f32>;\n\n struct Params {\n seq_len : u32,\n d_in : u32,\n d_out : u32,\n }\n @group(0) @binding(5) var<uniform> params : Params;\n\n // Unpack one signed 8-bit integer from a 32-bit packed block\n fn unpack_i8(packed: u32, byte_idx: u32) -> f32 {\n let val_i32 = extractBits(i32(packed), byte_idx * 8u, 8u);\n return f32(val_i32);\n }\n\n @compute @workgroup_size(64, 1, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let row = global_id.y;\n let col = global_id.x;\n \n if (row >= params.seq_len || col >= params.d_out) { return; }\n \n var sum : f32 = 0.0;\n let d_in_4 = params.d_in / 4u;\n \n for (var i = 0u; i < d_in_4; i = i + 1u) {\n let w_packed = weight_int8_packed[col * d_in_4 + i];\n let in_base = row * params.d_in + i * 4u;\n \n sum = sum + input[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n sum = sum + input[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n sum = sum + input[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n sum = sum + input[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n \n let scale = weight_scales[col];\n output[row * params.d_out + col] = (sum * scale) + bias[col];\n }\n";
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// shaders/conv2D.wgsl
|
|
33
|
+
var conv2D_default;
|
|
34
|
+
var init_conv2D = __esm({
|
|
35
|
+
"shaders/conv2D.wgsl"() {
|
|
36
|
+
conv2D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<f32>;\n@group(0) @binding(2) var<storage, read> bias : array<f32>;\n@group(0) @binding(3) var<storage, read_write> output : array<f32>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let z = gid.z;\n let nb = z / params.out_c;\n let oc = z - nb * params.out_c;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h) { return; }\n\n var sum = 0.0;\n if (params.groups == params.in_c) {\n let mult = params.out_c / params.in_c;\n let ic = oc / mult;\n let m = oc - ic * mult;\n for (var yy = 0u; yy < params.kh; yy = yy + 1u) {\n let iy = i32(oy * params.sy + yy * params.dy) - i32(params.pt);\n if (iy < 0 || iy >= i32(params.in_h)) { continue; }\n for (var xx = 0u; xx < params.kw; xx = xx + 1u) {\n let ix = i32(ox * params.sx + xx * params.dx) - i32(params.pl);\n if (ix < 0 || ix >= i32(params.in_w)) { continue; }\n let ii = ((nb * params.in_h + u32(iy)) * params.in_w + u32(ix)) * params.in_c + ic;\n let wi = (((yy * params.kw + xx) * params.in_c + ic) * mult) + m;\n sum = sum + input[ii] * weight[wi];\n }\n }\n } else {\n let out_per_g = params.out_c / params.groups;\n let in_per_g = params.in_c / params.groups;\n let g = oc / out_per_g;\n let ic0 = g * in_per_g;\n for (var icl = 0u; icl < in_per_g; icl = icl + 1u) {\n let ic = ic0 + icl;\n for (var yy = 0u; yy < params.kh; yy = yy + 1u) {\n let iy = i32(oy * params.sy + yy * params.dy) - i32(params.pt);\n if (iy < 0 || iy >= i32(params.in_h)) { continue; }\n for (var xx = 0u; xx < params.kw; xx = xx + 1u) {\n let ix = i32(ox * params.sx + xx * params.dx) - i32(params.pl);\n if (ix < 0 || ix >= i32(params.in_w)) { continue; }\n let ii = ((nb * params.in_h + u32(iy)) * params.in_w + u32(ix)) * params.in_c + ic;\n let wi = (((yy * params.kw + xx) * params.in_c + ic) * params.out_c) + oc;\n sum = sum + input[ii] * weight[wi];\n }\n }\n }\n }\n\n var v = sum + bias[oc];\n if (params.relu == 1u) {\n v = max(v, 0.0);\n } else if (params.relu >= 2u) {\n v = min(max(v, 0.0), 6.0);\n }\n output[((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc] = v;\n}\n";
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// shaders/conv2DDepthwise8.wgsl
|
|
41
|
+
var conv2DDepthwise8_default;
|
|
42
|
+
var init_conv2DDepthwise8 = __esm({
|
|
43
|
+
"shaders/conv2DDepthwise8.wgsl"() {
|
|
44
|
+
conv2DDepthwise8_default = "@group(0) @binding(0) var<storage, read> input : array<vec4<f32>>;\n@group(0) @binding(1) var<storage, read> weight : array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec4<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nfn apply_relu4(v_in : vec4<f32>) -> vec4<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec4<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec4<f32>(0.0)), vec4<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let c8_count = (params.out_c + 7u) / 8u;\n let nb = gid.z / c8_count;\n let ch0 = (gid.z - nb * c8_count) * 8u;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h || ch0 >= params.out_c) { return; }\n\n var sum0 = vec4<f32>(0.0);\n var sum1 = vec4<f32>(0.0);\n\n for (var yy = 0u; yy < params.kh; yy = yy + 1u) {\n let iy = i32(oy * params.sy + yy * params.dy) - i32(params.pt);\n if (iy < 0 || iy >= i32(params.in_h)) { continue; }\n for (var xx = 0u; xx < params.kw; xx = xx + 1u) {\n let ix = i32(ox * params.sx + xx * params.dx) - i32(params.pl);\n if (ix < 0 || ix >= i32(params.in_w)) { continue; }\n let input_base = (((nb * params.in_h + u32(iy)) * params.in_w + u32(ix)) * params.in_c + ch0) / 4u;\n let weight_base = (((yy * params.kw + xx) * params.in_c) + ch0) / 4u;\n sum0 = sum0 + input[input_base] * weight[weight_base];\n sum1 = sum1 + input[input_base + 1u] * weight[weight_base + 1u];\n }\n }\n\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + ch0) / 4u;\n let bias_base = ch0 / 4u;\n output[output_base] = apply_relu4(sum0 + bias[bias_base]);\n output[output_base + 1u] = apply_relu4(sum1 + bias[bias_base + 1u]);\n}\n";
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// shaders/conv2DPointwise16.wgsl
|
|
49
|
+
var conv2DPointwise16_default;
|
|
50
|
+
var init_conv2DPointwise16 = __esm({
|
|
51
|
+
"shaders/conv2DPointwise16.wgsl"() {
|
|
52
|
+
conv2DPointwise16_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec4<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nfn apply_relu4(v_in : vec4<f32>) -> vec4<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec4<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec4<f32>(0.0)), vec4<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let oc16_count = (params.out_c + 15u) / 16u;\n let nb = gid.z / oc16_count;\n let oc0 = (gid.z - nb * oc16_count) * 16u;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h || oc0 >= params.out_c) { return; }\n\n var sum0 = vec4<f32>(0.0);\n var sum1 = vec4<f32>(0.0);\n var sum2 = vec4<f32>(0.0);\n var sum3 = vec4<f32>(0.0);\n\n let input_base = ((nb * params.in_h + oy) * params.in_w + ox) * params.in_c;\n for (var ic = 0u; ic < params.in_c; ic = ic + 1u) {\n let x = input[input_base + ic];\n let wbase = (ic * params.out_c + oc0) / 4u;\n sum0 = sum0 + x * weight[wbase];\n sum1 = sum1 + x * weight[wbase + 1u];\n sum2 = sum2 + x * weight[wbase + 2u];\n sum3 = sum3 + x * weight[wbase + 3u];\n }\n\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc0) / 4u;\n let bias_base = oc0 / 4u;\n output[output_base] = apply_relu4(sum0 + bias[bias_base]);\n output[output_base + 1u] = apply_relu4(sum1 + bias[bias_base + 1u]);\n output[output_base + 2u] = apply_relu4(sum2 + bias[bias_base + 2u]);\n output[output_base + 3u] = apply_relu4(sum3 + bias[bias_base + 3u]);\n}\n";
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// shaders/conv2DPointwise16Tile.wgsl
|
|
57
|
+
var conv2DPointwise16Tile_default;
|
|
58
|
+
var init_conv2DPointwise16Tile = __esm({
|
|
59
|
+
"shaders/conv2DPointwise16Tile.wgsl"() {
|
|
60
|
+
conv2DPointwise16Tile_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec4<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nconst TILE_C : u32 = 64u;\nvar<workgroup> tile_weight : array<vec4<f32>, 256>;\n\nfn apply_relu4(v_in : vec4<f32>) -> vec4<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec4<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec4<f32>(0.0)), vec4<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>,\n @builtin(local_invocation_id) lid3 : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let oc16_count = params.out_c / 16u;\n let nb = gid.z / oc16_count;\n let oc0 = (gid.z - nb * oc16_count) * 16u;\n let lid = lid3.y * 8u + lid3.x;\n let in_bounds = nb < params.n && ox < params.out_w && oy < params.out_h;\n\n var sum0 = vec4<f32>(0.0);\n var sum1 = vec4<f32>(0.0);\n var sum2 = vec4<f32>(0.0);\n var sum3 = vec4<f32>(0.0);\n let input_base = ((nb * params.in_h + oy) * params.in_w + ox) * params.in_c;\n\n for (var tile_start = 0u; tile_start < params.in_c; tile_start = tile_start + TILE_C) {\n let tile_count = min(TILE_C, params.in_c - tile_start);\n for (var wi = lid; wi < tile_count * 4u; wi = wi + 64u) {\n let ic = tile_start + wi / 4u;\n let oc_vec = wi - (wi / 4u) * 4u;\n tile_weight[wi] = weight[(ic * params.out_c + oc0) / 4u + oc_vec];\n }\n workgroupBarrier();\n\n if (in_bounds) {\n for (var ti = 0u; ti < tile_count; ti = ti + 1u) {\n let x = input[input_base + tile_start + ti];\n let wbase = ti * 4u;\n sum0 = sum0 + x * tile_weight[wbase];\n sum1 = sum1 + x * tile_weight[wbase + 1u];\n sum2 = sum2 + x * tile_weight[wbase + 2u];\n sum3 = sum3 + x * tile_weight[wbase + 3u];\n }\n }\n workgroupBarrier();\n }\n\n if (!in_bounds) { return; }\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc0) / 4u;\n let bias_base = oc0 / 4u;\n output[output_base] = apply_relu4(sum0 + bias[bias_base]);\n output[output_base + 1u] = apply_relu4(sum1 + bias[bias_base + 1u]);\n output[output_base + 2u] = apply_relu4(sum2 + bias[bias_base + 2u]);\n output[output_base + 3u] = apply_relu4(sum3 + bias[bias_base + 3u]);\n}\n";
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// shaders/conv2DPointwise8Vec2.wgsl
|
|
65
|
+
var conv2DPointwise8Vec2_default;
|
|
66
|
+
var init_conv2DPointwise8Vec2 = __esm({
|
|
67
|
+
"shaders/conv2DPointwise8Vec2.wgsl"() {
|
|
68
|
+
conv2DPointwise8Vec2_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<vec2<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec2<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec2<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nfn apply_relu2(v_in : vec2<f32>) -> vec2<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec2<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec2<f32>(0.0)), vec2<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let oc8_count = (params.out_c + 7u) / 8u;\n let nb = gid.z / oc8_count;\n let oc0 = (gid.z - nb * oc8_count) * 8u;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h || oc0 >= params.out_c) { return; }\n\n let has1 = oc0 + 2u < params.out_c;\n let has2 = oc0 + 4u < params.out_c;\n let has3 = oc0 + 6u < params.out_c;\n var sum0 = vec2<f32>(0.0);\n var sum1 = vec2<f32>(0.0);\n var sum2 = vec2<f32>(0.0);\n var sum3 = vec2<f32>(0.0);\n\n let input_base = ((nb * params.in_h + oy) * params.in_w + ox) * params.in_c;\n for (var ic = 0u; ic < params.in_c; ic = ic + 1u) {\n let x = input[input_base + ic];\n let wbase = (ic * params.out_c + oc0) / 2u;\n sum0 = sum0 + x * weight[wbase];\n if (has1) { sum1 = sum1 + x * weight[wbase + 1u]; }\n if (has2) { sum2 = sum2 + x * weight[wbase + 2u]; }\n if (has3) { sum3 = sum3 + x * weight[wbase + 3u]; }\n }\n\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc0) / 2u;\n let bias_base = oc0 / 2u;\n output[output_base] = apply_relu2(sum0 + bias[bias_base]);\n if (has1) { output[output_base + 1u] = apply_relu2(sum1 + bias[bias_base + 1u]); }\n if (has2) { output[output_base + 2u] = apply_relu2(sum2 + bias[bias_base + 2u]); }\n if (has3) { output[output_base + 3u] = apply_relu2(sum3 + bias[bias_base + 3u]); }\n}\n";
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// shaders/conv2DPointwise8Vec4.wgsl
|
|
73
|
+
var conv2DPointwise8Vec4_default;
|
|
74
|
+
var init_conv2DPointwise8Vec4 = __esm({
|
|
75
|
+
"shaders/conv2DPointwise8Vec4.wgsl"() {
|
|
76
|
+
conv2DPointwise8Vec4_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec4<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nfn apply_relu4(v_in : vec4<f32>) -> vec4<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec4<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec4<f32>(0.0)), vec4<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let oc8_count = (params.out_c + 7u) / 8u;\n let nb = gid.z / oc8_count;\n let oc0 = (gid.z - nb * oc8_count) * 8u;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h || oc0 >= params.out_c) { return; }\n\n var sum0 = vec4<f32>(0.0);\n var sum1 = vec4<f32>(0.0);\n let input_base = ((nb * params.in_h + oy) * params.in_w + ox) * params.in_c;\n let has_second = oc0 + 4u < params.out_c;\n for (var ic = 0u; ic < params.in_c; ic = ic + 1u) {\n let x = input[input_base + ic];\n let wbase = (ic * params.out_c + oc0) / 4u;\n sum0 = sum0 + x * weight[wbase];\n if (has_second) {\n sum1 = sum1 + x * weight[wbase + 1u];\n }\n }\n\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc0) / 4u;\n let bias_base = oc0 / 4u;\n output[output_base] = apply_relu4(sum0 + bias[bias_base]);\n if (has_second) {\n output[output_base + 1u] = apply_relu4(sum1 + bias[bias_base + 1u]);\n }\n}\n";
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// shaders/conv2DRegularC3Out16.wgsl
|
|
81
|
+
var conv2DRegularC3Out16_default;
|
|
82
|
+
var init_conv2DRegularC3Out16 = __esm({
|
|
83
|
+
"shaders/conv2DRegularC3Out16.wgsl"() {
|
|
84
|
+
conv2DRegularC3Out16_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> weight : array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read> bias : array<vec4<f32>>;\n@group(0) @binding(3) var<storage, read_write> output : array<vec4<f32>>;\n\nstruct Params {\n n : u32,\n in_h : u32,\n in_w : u32,\n in_c : u32,\n out_c : u32,\n out_h : u32,\n out_w : u32,\n kh : u32,\n kw : u32,\n sy : u32,\n sx : u32,\n pt : u32,\n pl : u32,\n groups : u32,\n relu : u32,\n dy : u32,\n dx : u32,\n}\n@group(0) @binding(4) var<uniform> params : Params;\n\nfn apply_relu4(v_in : vec4<f32>) -> vec4<f32> {\n var v = v_in;\n if (params.relu == 1u) {\n v = max(v, vec4<f32>(0.0));\n } else if (params.relu >= 2u) {\n v = min(max(v, vec4<f32>(0.0)), vec4<f32>(6.0));\n }\n return v;\n}\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let oc16_count = params.out_c / 16u;\n let nb = gid.z / oc16_count;\n let oc0 = (gid.z - nb * oc16_count) * 16u;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h) { return; }\n\n var sum0 = vec4<f32>(0.0);\n var sum1 = vec4<f32>(0.0);\n var sum2 = vec4<f32>(0.0);\n var sum3 = vec4<f32>(0.0);\n for (var yy = 0u; yy < params.kh; yy = yy + 1u) {\n let iy = i32(oy * params.sy + yy * params.dy) - i32(params.pt);\n if (iy < 0 || iy >= i32(params.in_h)) { continue; }\n for (var xx = 0u; xx < params.kw; xx = xx + 1u) {\n let ix = i32(ox * params.sx + xx * params.dx) - i32(params.pl);\n if (ix < 0 || ix >= i32(params.in_w)) { continue; }\n let input_base = ((nb * params.in_h + u32(iy)) * params.in_w + u32(ix)) * 3u;\n let weight_base = (((yy * params.kw + xx) * 3u) * params.out_c + oc0) / 4u;\n for (var ic = 0u; ic < 3u; ic = ic + 1u) {\n let x = input[input_base + ic];\n let wbase = weight_base + ic * (params.out_c / 4u);\n sum0 = sum0 + x * weight[wbase];\n sum1 = sum1 + x * weight[wbase + 1u];\n sum2 = sum2 + x * weight[wbase + 2u];\n sum3 = sum3 + x * weight[wbase + 3u];\n }\n }\n }\n\n let output_base = (((nb * params.out_h + oy) * params.out_w + ox) * params.out_c + oc0) / 4u;\n let bias_base = oc0 / 4u;\n output[output_base] = apply_relu4(sum0 + bias[bias_base]);\n output[output_base + 1u] = apply_relu4(sum1 + bias[bias_base + 1u]);\n output[output_base + 2u] = apply_relu4(sum2 + bias[bias_base + 2u]);\n output[output_base + 3u] = apply_relu4(sum3 + bias[bias_base + 3u]);\n}\n";
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// shaders/layerNorm.wgsl
|
|
89
|
+
var layerNorm_default;
|
|
90
|
+
var init_layerNorm = __esm({
|
|
91
|
+
"shaders/layerNorm.wgsl"() {
|
|
92
|
+
layerNorm_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read> bias : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n \n struct Params { rows : u32, d_model : u32 }\n @group(0) @binding(4) var<uniform> params : Params;\n\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let row = global_id.x;\n if (row >= params.rows) { return; }\n let d_model = params.d_model;\n let offset = row * d_model;\n \n var sum : f32 = 0.0;\n var sq_sum : f32 = 0.0;\n \n for (var i = 0u; i < d_model; i = i + 1u) {\n let val = input[offset + i];\n sum = sum + val;\n sq_sum = sq_sum + (val * val);\n }\n \n let mean = sum / f32(d_model);\n let variance = (sq_sum / f32(d_model)) - (mean * mean);\n let inv_std = inverseSqrt(variance + 1e-5);\n \n for (var i = 0u; i < d_model; i = i + 1u) {\n let norm_val = (input[offset + i] - mean) * inv_std;\n output[offset + i] = norm_val * weight[i] + bias[i];\n }\n }\n";
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// shaders/binaryBroadcast.wgsl
|
|
97
|
+
var binaryBroadcast_default;
|
|
98
|
+
var init_binaryBroadcast = __esm({
|
|
99
|
+
"shaders/binaryBroadcast.wgsl"() {
|
|
100
|
+
binaryBroadcast_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { size : u32, is_b_scalar : u32, b_size : u32, a_size: u32, is_a_scalar: u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n \n var a_val : f32 = 0.0;\n if (params.is_a_scalar == 1u) {\n a_val = a[0];\n } else if (params.a_size < params.size && params.a_size > 0u) {\n a_val = a[idx % params.a_size];\n } else {\n a_val = a[idx];\n }\n \n var b_val : f32 = 0.0;\n if (params.is_b_scalar == 1u) {\n b_val = b[0];\n } else if (params.b_size < params.size && params.b_size > 0u) {\n b_val = b[idx % params.b_size];\n } else {\n b_val = b[idx];\n }\n \n var out_val : f32 = 0.0;\n undefined\n output[idx] = out_val;\n }\n";
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// shaders/elementwise.wgsl
|
|
105
|
+
var elementwise_default;
|
|
106
|
+
var init_elementwise = __esm({
|
|
107
|
+
"shaders/elementwise.wgsl"() {
|
|
108
|
+
elementwise_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n undefined\n output[idx] = out_val;\n }\n";
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// shaders/resize.wgsl
|
|
113
|
+
var resize_default;
|
|
114
|
+
var init_resize = __esm({
|
|
115
|
+
"shaders/resize.wgsl"() {
|
|
116
|
+
resize_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n\nstruct Params {\n n : u32,\n h : u32,\n w : u32,\n c : u32,\n out_h : u32,\n out_w : u32,\n mode : u32,\n}\n@group(0) @binding(2) var<uniform> params : Params;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let z = gid.z;\n let nb = z / params.c;\n let ch = z - nb * params.c;\n if (nb >= params.n || ox >= params.out_w || oy >= params.out_h) { return; }\n\n if (params.mode == 0u) {\n let iy = (oy * params.h) / params.out_h;\n let ix = (ox * params.w) / params.out_w;\n output[((nb * params.out_h + oy) * params.out_w + ox) * params.c + ch] =\n input[((nb * params.h + iy) * params.w + ix) * params.c + ch];\n return;\n }\n\n let scale_y = f32(params.h) / f32(params.out_h);\n let scale_x = f32(params.w) / f32(params.out_w);\n var fy = (f32(oy) + 0.5) * scale_y - 0.5;\n var fx = (f32(ox) + 0.5) * scale_x - 0.5;\n if (fy < 0.0) { fy = 0.0; }\n if (fx < 0.0) { fx = 0.0; }\n let y0 = u32(fy);\n let x0 = u32(fx);\n let y1 = min(y0 + 1u, params.h - 1u);\n let x1 = min(x0 + 1u, params.w - 1u);\n let dy = fy - f32(y0);\n let dx = fx - f32(x0);\n let base = nb * params.h * params.w * params.c;\n let v00 = input[base + (y0 * params.w + x0) * params.c + ch];\n let v01 = input[base + (y0 * params.w + x1) * params.c + ch];\n let v10 = input[base + (y1 * params.w + x0) * params.c + ch];\n let v11 = input[base + (y1 * params.w + x1) * params.c + ch];\n let val = v00 * (1.0 - dy) * (1.0 - dx) + v01 * (1.0 - dy) * dx +\n v10 * dy * (1.0 - dx) + v11 * dy * dx;\n output[((nb * params.out_h + oy) * params.out_w + ox) * params.c + ch] = val;\n}\n";
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// shaders/slice.wgsl
|
|
121
|
+
var slice_default;
|
|
122
|
+
var init_slice = __esm({
|
|
123
|
+
"shaders/slice.wgsl"() {
|
|
124
|
+
slice_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n// General strided slice over 4 dims (leading dims are padded to 1). For each\n// output element the source coord along axis k is start[k] + out_coord * step[k].\nstruct Params {\n out_b: u32, out_c: u32, out_h: u32, out_w: u32,\n in_c: u32, in_h: u32, in_w: u32,\n s0: u32, s1: u32, s2: u32, s3: u32,\n st0: u32, st1: u32, st2: u32, st3: u32,\n total: u32\n}\n@group(0) @binding(2) var<uniform> p : Params;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let idx = gid.x;\n if (idx >= p.total) { return; }\n let ow = idx % p.out_w;\n let oh = (idx / p.out_w) % p.out_h;\n let oc = (idx / (p.out_w * p.out_h)) % p.out_c;\n let ob = idx / (p.out_w * p.out_h * p.out_c);\n let ib = p.s0 + ob * p.st0;\n let ic = p.s1 + oc * p.st1;\n let ih = p.s2 + oh * p.st2;\n let iw = p.s3 + ow * p.st3;\n output[idx] = input[((ib * p.in_c + ic) * p.in_h + ih) * p.in_w + iw];\n}\n";
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// shaders/sub.wgsl
|
|
129
|
+
var sub_default;
|
|
130
|
+
var init_sub = __esm({
|
|
131
|
+
"shaders/sub.wgsl"() {
|
|
132
|
+
sub_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { size : u32, is_b_scalar : u32, b_size : u32, a_size: u32, is_a_scalar: u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n \n var a_val : f32 = 0.0;\n if (params.is_a_scalar == 1u) {\n a_val = a[0];\n } else if (params.a_size < params.size && params.a_size > 0u) {\n a_val = a[idx % params.a_size];\n } else {\n a_val = a[idx];\n }\n \n var b_val : f32 = 0.0;\n if (params.is_b_scalar == 1u) {\n b_val = b[0];\n } else if (params.b_size < params.size && params.b_size > 0u) {\n b_val = b[idx % params.b_size];\n } else {\n b_val = b[idx];\n }\n \n var out_val : f32 = 0.0;\n out_val = a_val - b_val;\n output[idx] = out_val;\n }\n";
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// shaders/div.wgsl
|
|
137
|
+
var div_default;
|
|
138
|
+
var init_div = __esm({
|
|
139
|
+
"shaders/div.wgsl"() {
|
|
140
|
+
div_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { size : u32, is_b_scalar : u32, b_size : u32, a_size: u32, is_a_scalar: u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n \n var a_val : f32 = 0.0;\n if (params.is_a_scalar == 1u) {\n a_val = a[0];\n } else if (params.a_size < params.size && params.a_size > 0u) {\n a_val = a[idx % params.a_size];\n } else {\n a_val = a[idx];\n }\n \n var b_val : f32 = 0.0;\n if (params.is_b_scalar == 1u) {\n b_val = b[0];\n } else if (params.b_size < params.size && params.b_size > 0u) {\n b_val = b[idx % params.b_size];\n } else {\n b_val = b[idx];\n }\n \n var out_val : f32 = 0.0;\n out_val = a_val / b_val;\n output[idx] = out_val;\n }\n";
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// shaders/siLU.wgsl
|
|
145
|
+
var siLU_default;
|
|
146
|
+
var init_siLU = __esm({
|
|
147
|
+
"shaders/siLU.wgsl"() {
|
|
148
|
+
siLU_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n out_val = x * (1.0 / (1.0 + exp(-x)));\n output[idx] = out_val;\n }\n";
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// shaders/leakyReLU.wgsl
|
|
153
|
+
var leakyReLU_default;
|
|
154
|
+
var init_leakyReLU = __esm({
|
|
155
|
+
"shaders/leakyReLU.wgsl"() {
|
|
156
|
+
leakyReLU_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32, alpha : f32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n if (x > 0.0) { output[idx] = x; } else { output[idx] = x * params.alpha; }\n }\n";
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// shaders/tanh.wgsl
|
|
161
|
+
var tanh_default;
|
|
162
|
+
var init_tanh = __esm({
|
|
163
|
+
"shaders/tanh.wgsl"() {
|
|
164
|
+
tanh_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n let e2x = exp(2.0 * x); out_val = (e2x - 1.0) / (e2x + 1.0);\n output[idx] = out_val;\n }\n";
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// shaders/clip.wgsl
|
|
169
|
+
var clip_default;
|
|
170
|
+
var init_clip = __esm({
|
|
171
|
+
"shaders/clip.wgsl"() {
|
|
172
|
+
clip_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32, min_v : f32, max_v : f32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n output[idx] = clamp(input[idx], params.min_v, params.max_v);\n }\n";
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// shaders/rMSNorm.wgsl
|
|
177
|
+
var rMSNorm_default;
|
|
178
|
+
var init_rMSNorm = __esm({
|
|
179
|
+
"shaders/rMSNorm.wgsl"() {
|
|
180
|
+
rMSNorm_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n struct Params { seq_len : u32, d_model : u32, eps : f32 }\n @group(0) @binding(3) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let s = global_id.x;\n if (s >= params.seq_len) { return; }\n var var_val = 0.0;\n let offset = s * params.d_model;\n for (var d = 0u; d < params.d_model; d = d + 1u) {\n let v = input[offset + d];\n var_val = var_val + v * v;\n }\n var_val = var_val / f32(params.d_model);\n let inv_std = 1.0 / sqrt(var_val + params.eps);\n for (var d = 0u; d < params.d_model; d = d + 1u) {\n output[offset + d] = input[offset + d] * inv_std * weight[d];\n }\n }\n";
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// shaders/softmax.wgsl
|
|
185
|
+
var softmax_default;
|
|
186
|
+
var init_softmax = __esm({
|
|
187
|
+
"shaders/softmax.wgsl"() {
|
|
188
|
+
softmax_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { b : u32, d : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let i = global_id.x;\n if (i >= params.b) { return; }\n let offset = i * params.d;\n var max_val = -100000.0;\n for (var j = 0u; j < params.d; j = j + 1u) {\n if (input[offset + j] > max_val) { max_val = input[offset + j]; }\n }\n var sum = 0.0;\n for (var j = 0u; j < params.d; j = j + 1u) {\n let e = exp(input[offset + j] - max_val);\n output[offset + j] = e;\n sum = sum + e;\n }\n for (var j = 0u; j < params.d; j = j + 1u) {\n output[offset + j] = output[offset + j] / sum;\n }\n }\n";
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// shaders/pReLU.wgsl
|
|
193
|
+
var pReLU_default;
|
|
194
|
+
var init_pReLU = __esm({
|
|
195
|
+
"shaders/pReLU.wgsl"() {
|
|
196
|
+
pReLU_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n struct Params { size : u32, c : u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let c_idx = idx % params.c;\n let alpha = weight[c_idx];\n let v = input[idx];\n if (v > 0.0) { output[idx] = v; } else { output[idx] = v * alpha; }\n }\n";
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// shaders/logSoftmax.wgsl
|
|
201
|
+
var logSoftmax_default;
|
|
202
|
+
var init_logSoftmax = __esm({
|
|
203
|
+
"shaders/logSoftmax.wgsl"() {
|
|
204
|
+
logSoftmax_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { b : u32, d : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let i = global_id.x;\n if (i >= params.b) { return; }\n let offset = i * params.d;\n var max_val = -100000.0;\n for (var j = 0u; j < params.d; j = j + 1u) {\n if (input[offset + j] > max_val) { max_val = input[offset + j]; }\n }\n var sum = 0.0;\n for (var j = 0u; j < params.d; j = j + 1u) {\n sum = sum + exp(input[offset + j] - max_val);\n }\n let logSum = log(sum);\n for (var j = 0u; j < params.d; j = j + 1u) {\n output[offset + j] = (input[offset + j] - max_val) - logSum;\n }\n }\n";
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// shaders/reduce.wgsl
|
|
209
|
+
var reduce_default;
|
|
210
|
+
var init_reduce = __esm({
|
|
211
|
+
"shaders/reduce.wgsl"() {
|
|
212
|
+
reduce_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n// Reduce over the last (innermost) axis: `b` rows of length `d` -> `b` outputs.\n// `inv` scales the sum: 1.0 for ReduceSum, 1.0/d for ReduceMean.\nstruct Params { b : u32, d : u32, inv : f32 }\n@group(0) @binding(2) var<uniform> p : Params;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let i = gid.x;\n if (i >= p.b) { return; }\n let offset = i * p.d;\n var sum = 0.0;\n for (var j = 0u; j < p.d; j = j + 1u) {\n sum = sum + input[offset + j];\n }\n output[i] = sum * p.inv;\n}\n";
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// shaders/averagePool2D.wgsl
|
|
217
|
+
var averagePool2D_default;
|
|
218
|
+
var init_averagePool2D = __esm({
|
|
219
|
+
"shaders/averagePool2D.wgsl"() {
|
|
220
|
+
averagePool2D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { b: u32, in_h: u32, in_w: u32, c: u32, out_h: u32, out_w: u32, kh: u32, kw: u32, sh: u32, sw: u32, ph: u32, pw: u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(8, 8, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x; let y = global_id.y; let z = global_id.z;\n let b = z / params.c;\n let c = z - b * params.c;\n if (x >= params.out_w || y >= params.out_h || b >= params.b) { return; }\n var sum = 0.0; var count = 0u;\n for (var ky = 0u; ky < params.kh; ky = ky + 1u) {\n for (var kx = 0u; kx < params.kw; kx = kx + 1u) {\n let in_y = i32(y * params.sh) - i32(params.ph) + i32(ky);\n let in_x = i32(x * params.sw) - i32(params.pw) + i32(kx);\n if (in_y >= 0 && in_y < i32(params.in_h) && in_x >= 0 && in_x < i32(params.in_w)) {\n sum = sum + input[((b * params.in_h + u32(in_y)) * params.in_w + u32(in_x)) * params.c + c];\n count = count + 1u;\n }\n }\n }\n if (count == 0u) { count = 1u; }\n output[((b * params.out_h + y) * params.out_w + x) * params.c + c] = sum / f32(count);\n }\n";
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// shaders/gather.wgsl
|
|
225
|
+
var gather_default;
|
|
226
|
+
var init_gather = __esm({
|
|
227
|
+
"shaders/gather.wgsl"() {
|
|
228
|
+
gather_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read> indices : array<f32>;\n@group(0) @binding(2) var<storage, read_write> output : array<f32>;\n// Gather along axis 0: output[i, ...] = input[indices[i], ...]. `row_size` is the\n// number of contiguous elements per gathered row (product of input dims after\n// axis 0); `num_idx` is the number of indices. total = num_idx * row_size.\nstruct Params { row_size : u32, num_idx : u32, total : u32 }\n@group(0) @binding(3) var<uniform> p : Params;\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let idx = gid.x;\n if (idx >= p.total) { return; }\n let k = idx % p.row_size;\n let i = idx / p.row_size;\n let row = u32(indices[i]);\n output[idx] = input[row * p.row_size + k];\n}\n";
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// shaders/where.wgsl
|
|
233
|
+
var where_default;
|
|
234
|
+
var init_where = __esm({
|
|
235
|
+
"shaders/where.wgsl"() {
|
|
236
|
+
where_default = "@group(0) @binding(0) var<storage, read> cond : array<f32>;\n @group(0) @binding(1) var<storage, read> a : array<f32>;\n @group(0) @binding(2) var<storage, read> b : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(4) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n if (cond[idx] != 0.0) { output[idx] = a[idx]; } else { output[idx] = b[idx]; }\n }\n";
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// shaders/dequantizeLinear.wgsl
|
|
241
|
+
var dequantizeLinear_default;
|
|
242
|
+
var init_dequantizeLinear = __esm({
|
|
243
|
+
"shaders/dequantizeLinear.wgsl"() {
|
|
244
|
+
dequantizeLinear_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> scale : array<f32>;\n @group(0) @binding(2) var<storage, read> zero_point : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n struct Params { size : u32, has_zp : u32 }\n @group(0) @binding(4) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n var zp = 0.0;\n if (params.has_zp == 1u) { zp = zero_point[0]; }\n output[idx] = (input[idx] - zp) * scale[0];\n }\n";
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// shaders/expand.wgsl
|
|
249
|
+
var expand_default;
|
|
250
|
+
var init_expand = __esm({
|
|
251
|
+
"shaders/expand.wgsl"() {
|
|
252
|
+
expand_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { in_b: u32, in_h: u32, in_w: u32, in_c: u32, out_b: u32, out_h: u32, out_w: u32, out_c: u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n let total = params.out_b * params.out_h * params.out_w * params.out_c;\n if (idx >= total) { return; }\n let oc = idx % params.out_c;\n let ow = (idx / params.out_c) % params.out_w;\n let oh = (idx / (params.out_c * params.out_w)) % params.out_h;\n let ob = idx / (params.out_c * params.out_w * params.out_h);\n let ib = ob % params.in_b;\n let ih = oh % params.in_h; let iw = ow % params.in_w;\n let ic = oc % params.in_c;\n output[idx] = input[((ib * params.in_h + ih) * params.in_w + iw) * params.in_c + ic];\n }\n";
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// shaders/pad.wgsl
|
|
257
|
+
var pad_default;
|
|
258
|
+
var init_pad = __esm({
|
|
259
|
+
"shaders/pad.wgsl"() {
|
|
260
|
+
pad_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { b: u32, in_h: u32, in_w: u32, c: u32, out_h: u32, out_w: u32, pt: u32, pl: u32, val: f32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n let total = params.b * params.out_h * params.out_w * params.c;\n if (idx >= total) { return; }\n let c = idx % params.c;\n let x = (idx / params.c) % params.out_w;\n let y = (idx / (params.c * params.out_w)) % params.out_h;\n let b = idx / (params.c * params.out_w * params.out_h);\n if (y >= params.pt && y < params.pt + params.in_h && x >= params.pl && x < params.pl + params.in_w) {\n output[idx] = input[((b * params.in_h + (y - params.pt)) * params.in_w + (x - params.pl)) * params.c + c];\n } else {\n output[idx] = params.val;\n }\n }\n";
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// shaders/convTranspose2D.wgsl
|
|
265
|
+
var convTranspose2D_default;
|
|
266
|
+
var init_convTranspose2D = __esm({
|
|
267
|
+
"shaders/convTranspose2D.wgsl"() {
|
|
268
|
+
convTranspose2D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read> bias : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n struct Params { b: u32, in_h: u32, in_w: u32, in_c: u32, out_h: u32, out_w: u32, out_c: u32, kh: u32, kw: u32, sh: u32, sw: u32, ph: u32, pw: u32, has_bias: u32 }\n @group(0) @binding(4) var<uniform> params : Params;\n @compute @workgroup_size(8, 8, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x; let y = global_id.y; let batch_c = global_id.z;\n if (x >= params.out_w || y >= params.out_h || batch_c >= (params.b * params.out_c)) { return; }\n let ob = batch_c / params.out_c;\n let oc = batch_c % params.out_c;\n var sum = 0.0;\n if (params.has_bias == 1u) { sum = bias[oc]; }\n for (var ic = 0u; ic < params.in_c; ic = ic + 1u) {\n for (var ky = 0u; ky < params.kh; ky = ky + 1u) {\n for (var kx = 0u; kx < params.kw; kx = kx + 1u) {\n let oy_shifted = i32(y) + i32(params.ph) - i32(ky);\n let ox_shifted = i32(x) + i32(params.pw) - i32(kx);\n if (oy_shifted % i32(params.sh) == 0 && ox_shifted % i32(params.sw) == 0) {\n let iy = oy_shifted / i32(params.sh);\n let ix = ox_shifted / i32(params.sw);\n if (iy >= 0 && iy < i32(params.in_h) && ix >= 0 && ix < i32(params.in_w)) {\n let in_val = input[((ob * params.in_h + u32(iy)) * params.in_w + u32(ix)) * params.in_c + ic];\n let w_val = weight[ic * (params.out_c * params.kh * params.kw) + oc * (params.kh * params.kw) + ky * params.kw + kx];\n sum = sum + in_val * w_val;\n }\n }\n }\n }\n }\n output[((ob * params.out_h + y) * params.out_w + x) * params.out_c + oc] = sum;\n }\n";
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// shaders/reLU.wgsl
|
|
273
|
+
var reLU_default;
|
|
274
|
+
var init_reLU = __esm({
|
|
275
|
+
"shaders/reLU.wgsl"() {
|
|
276
|
+
reLU_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n if (x > 0.0) { out_val = x; } else { out_val = 0.0; }\n output[idx] = out_val;\n }\n";
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// shaders/sigmoid.wgsl
|
|
281
|
+
var sigmoid_default;
|
|
282
|
+
var init_sigmoid = __esm({
|
|
283
|
+
"shaders/sigmoid.wgsl"() {
|
|
284
|
+
sigmoid_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n out_val = 1.0 / (1.0 + exp(-x));\n output[idx] = out_val;\n }\n";
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// shaders/hardSwish.wgsl
|
|
289
|
+
var hardSwish_default;
|
|
290
|
+
var init_hardSwish = __esm({
|
|
291
|
+
"shaders/hardSwish.wgsl"() {
|
|
292
|
+
hardSwish_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n var v = x + 3.0; if (v < 0.0) { v = 0.0; } if (v > 6.0) { v = 6.0; } out_val = x * v / 6.0;\n output[idx] = out_val;\n }\n";
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// shaders/hardSigmoid.wgsl
|
|
297
|
+
var hardSigmoid_default;
|
|
298
|
+
var init_hardSigmoid = __esm({
|
|
299
|
+
"shaders/hardSigmoid.wgsl"() {
|
|
300
|
+
hardSigmoid_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n var v = x + 3.0; if (v < 0.0) { v = 0.0; } if (v > 6.0) { v = 6.0; } out_val = v / 6.0;\n output[idx] = out_val;\n }\n";
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// shaders/copy.wgsl
|
|
305
|
+
var copy_default;
|
|
306
|
+
var init_copy = __esm({
|
|
307
|
+
"shaders/copy.wgsl"() {
|
|
308
|
+
copy_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n var out_val = x;\n out_val = x;\n output[idx] = out_val;\n }\n";
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// shaders/batchNorm2D.wgsl
|
|
313
|
+
var batchNorm2D_default;
|
|
314
|
+
var init_batchNorm2D = __esm({
|
|
315
|
+
"shaders/batchNorm2D.wgsl"() {
|
|
316
|
+
batchNorm2D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read> bias : array<f32>;\n @group(0) @binding(3) var<storage, read> running_mean : array<f32>;\n @group(0) @binding(4) var<storage, read> running_var : array<f32>;\n @group(0) @binding(5) var<storage, read_write> output : array<f32>;\n struct Params { b : u32, c : u32, h : u32, w : u32, eps : f32 }\n @group(0) @binding(6) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n let total = params.b * params.c * params.h * params.w;\n if (idx >= total) { return; }\n let c = idx % params.c;\n let mean = running_mean[c];\n let var_val = running_var[c];\n let gamma = weight[c];\n let beta = bias[c];\n let inv_std = 1.0 / sqrt(var_val + params.eps);\n output[idx] = (input[idx] - mean) * inv_std * gamma + beta;\n }\n";
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// shaders/gELU.wgsl
|
|
321
|
+
var gELU_default;
|
|
322
|
+
var init_gELU = __esm({
|
|
323
|
+
"shaders/gELU.wgsl"() {
|
|
324
|
+
gELU_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\nstruct Params { size : u32 }\n@group(0) @binding(2) var<uniform> params : Params;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n let x = input[idx];\n let cdf = 0.5 * (1.0 + tanh(0.7978845608 * (x + 0.044715 * x * x * x)));\n output[idx] = x * cdf;\n}\n";
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// shaders/add.wgsl
|
|
329
|
+
var add_default;
|
|
330
|
+
var init_add = __esm({
|
|
331
|
+
"shaders/add.wgsl"() {
|
|
332
|
+
add_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { size : u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx >= params.size) { return; }\n output[idx] = a[idx] + b[idx];\n }\n";
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// shaders/upsample2x.wgsl
|
|
337
|
+
var upsample2x_default;
|
|
338
|
+
var init_upsample2x = __esm({
|
|
339
|
+
"shaders/upsample2x.wgsl"() {
|
|
340
|
+
upsample2x_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n\nstruct Params {\n n : u32,\n h : u32,\n w : u32,\n c : u32,\n}\n@group(0) @binding(2) var<uniform> params : Params;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let z = gid.z;\n let nb = z / params.c;\n let ch = z - nb * params.c;\n let out_h = params.h * 2u;\n let out_w = params.w * 2u;\n if (nb >= params.n || ox >= out_w || oy >= out_h) { return; }\n output[((nb * out_h + oy) * out_w + ox) * params.c + ch] =\n input[((nb * params.h + (oy / 2u)) * params.w + (ox / 2u)) * params.c + ch];\n}\n";
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// shaders/concatCopy.wgsl
|
|
345
|
+
var concatCopy_default;
|
|
346
|
+
var init_concatCopy = __esm({
|
|
347
|
+
"shaders/concatCopy.wgsl"() {
|
|
348
|
+
concatCopy_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { size : u32, offset : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let i = gid.x;\n if (i >= params.size) { return; }\n output[params.offset + i] = input[i];\n }\n";
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// shaders/concat2.wgsl
|
|
353
|
+
var concat2_default;
|
|
354
|
+
var init_concat2 = __esm({
|
|
355
|
+
"shaders/concat2.wgsl"() {
|
|
356
|
+
concat2_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { a_size : u32, b_size : u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let idx = global_id.x;\n if (idx < params.a_size) {\n output[idx] = a[idx];\n } else if (idx < params.a_size + params.b_size) {\n output[idx] = b[idx - params.a_size];\n }\n }\n";
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// shaders/broadcastBinary.wgsl
|
|
361
|
+
var broadcastBinary_default;
|
|
362
|
+
var init_broadcastBinary = __esm({
|
|
363
|
+
"shaders/broadcastBinary.wgsl"() {
|
|
364
|
+
broadcastBinary_default = "@group(0) @binding(0) var<storage, read> a : array<f32>;\n @group(0) @binding(1) var<storage, read> b : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n @group(0) @binding(3) var<storage, read> md : array<u32>;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let idx = gid.x;\n let total = md[0];\n if (idx >= total) { return; }\n let rank = md[1];\n var rem = idx;\n var a_idx = 0u;\n var b_idx = 0u;\n for (var d = 0u; d < rank; d = d + 1u) {\n let os = md[2u + d];\n let coord = rem / os;\n rem = rem - coord * os;\n a_idx = a_idx + coord * md[2u + rank + d];\n b_idx = b_idx + coord * md[2u + 2u * rank + d];\n }\n let av = a[a_idx];\n let bv = b[b_idx];\n var out_val = 0.0;\n //__BINOP__\n output[idx] = out_val;\n }\n";
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// shaders/generalTranspose.wgsl
|
|
369
|
+
var generalTranspose_default;
|
|
370
|
+
var init_generalTranspose = __esm({
|
|
371
|
+
"shaders/generalTranspose.wgsl"() {
|
|
372
|
+
generalTranspose_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n @group(0) @binding(2) var<storage, read> md : array<u32>;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let idx = gid.x;\n let total = md[0];\n if (idx >= total) { return; }\n let rank = md[1];\n var rem = idx;\n var in_idx = 0u;\n for (var d = 0u; d < rank; d = d + 1u) {\n let os = md[2u + d];\n let coord = rem / os;\n rem = rem - coord * os;\n in_idx = in_idx + coord * md[2u + rank + d];\n }\n output[idx] = input[in_idx];\n }\n";
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// shaders/split.wgsl
|
|
377
|
+
var split_default;
|
|
378
|
+
var init_split = __esm({
|
|
379
|
+
"shaders/split.wgsl"() {
|
|
380
|
+
split_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n struct Params { total : u32, inner : u32, split_size : u32, axis_in : u32, offset : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let i = gid.x;\n if (i >= params.total) { return; }\n let inner_idx = i % params.inner;\n let s = (i / params.inner) % params.split_size;\n let outer_idx = i / (params.split_size * params.inner);\n let in_idx = outer_idx * (params.axis_in * params.inner)\n + (params.offset + s) * params.inner + inner_idx;\n output[i] = input[in_idx];\n }\n";
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// shaders/profileY.wgsl
|
|
385
|
+
var profileY_default;
|
|
386
|
+
var init_profileY = __esm({
|
|
387
|
+
"shaders/profileY.wgsl"() {
|
|
388
|
+
profileY_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n \n struct Params { in_h : u32, in_w : u32, in_c : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let y = global_id.x;\n let c = global_id.y;\n \n if (y >= params.in_h || c >= params.in_c) { return; }\n \n var max_val = -1e38;\n var sum_val = 0.0;\n \n for (var x = 0u; x < params.in_w; x = x + 1u) {\n let val = input[(y * params.in_w + x) * params.in_c + c];\n if (val > max_val) { max_val = val; }\n sum_val = sum_val + val;\n }\n \n let out_max_idx = c * params.in_h + y;\n let out_mean_idx = (c + params.in_c) * params.in_h + y;\n \n output[out_max_idx] = max_val;\n output[out_mean_idx] = sum_val / f32(params.in_w);\n }\n";
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// shaders/profileX.wgsl
|
|
393
|
+
var profileX_default;
|
|
394
|
+
var init_profileX = __esm({
|
|
395
|
+
"shaders/profileX.wgsl"() {
|
|
396
|
+
profileX_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n \n struct Params { in_h : u32, in_w : u32, in_c : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x;\n let c = global_id.y;\n \n if (x >= params.in_w || c >= params.in_c) { return; }\n \n var max_val = -1e38;\n var sum_val = 0.0;\n \n for (var y = 0u; y < params.in_h; y = y + 1u) {\n let val = input[(y * params.in_w + x) * params.in_c + c];\n if (val > max_val) { max_val = val; }\n sum_val = sum_val + val;\n }\n \n let out_max_idx = c * params.in_w + x;\n let out_mean_idx = (c + params.in_c) * params.in_w + x;\n \n output[out_max_idx] = max_val;\n output[out_mean_idx] = sum_val / f32(params.in_h);\n }\n";
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// shaders/globalAveragePool.wgsl
|
|
401
|
+
var globalAveragePool_default;
|
|
402
|
+
var init_globalAveragePool = __esm({
|
|
403
|
+
"shaders/globalAveragePool.wgsl"() {
|
|
404
|
+
globalAveragePool_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n\nstruct Params { n : u32, h : u32, w : u32, c : u32 }\n@group(0) @binding(2) var<uniform> params : Params;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ch = gid.x;\n let nb = gid.y;\n if (nb >= params.n || ch >= params.c) { return; }\n var sum = 0.0;\n for (var y = 0u; y < params.h; y = y + 1u) {\n for (var x = 0u; x < params.w; x = x + 1u) {\n sum = sum + input[((nb * params.h + y) * params.w + x) * params.c + ch];\n }\n }\n output[nb * params.c + ch] = sum / f32(params.h * params.w);\n}\n";
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
// shaders/meanHeight.wgsl
|
|
409
|
+
var meanHeight_default;
|
|
410
|
+
var init_meanHeight = __esm({
|
|
411
|
+
"shaders/meanHeight.wgsl"() {
|
|
412
|
+
meanHeight_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n\n struct Params { in_h : u32, in_w : u32, in_c : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x;\n let c = global_id.y;\n if (x >= params.in_w || c >= params.in_c) { return; }\n\n var sum_val = 0.0;\n for (var y = 0u; y < params.in_h; y = y + 1u) {\n sum_val = sum_val + input[(y * params.in_w + x) * params.in_c + c];\n }\n output[c * params.in_w + x] = sum_val / f32(params.in_h);\n }\n";
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// shaders/maxPool2D.wgsl
|
|
417
|
+
var maxPool2D_default;
|
|
418
|
+
var init_maxPool2D = __esm({
|
|
419
|
+
"shaders/maxPool2D.wgsl"() {
|
|
420
|
+
maxPool2D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n@group(0) @binding(1) var<storage, read_write> output : array<f32>;\n\nstruct Params {\n h : u32,\n w : u32,\n c : u32,\n out_h : u32,\n out_w : u32,\n ky : u32,\n kx : u32,\n sy : u32,\n sx : u32,\n py : u32,\n px : u32,\n}\n@group(0) @binding(2) var<uniform> params : Params;\n\n@compute @workgroup_size(8, 8, 1)\nfn main(@builtin(global_invocation_id) gid : vec3<u32>) {\n let ox = gid.x;\n let oy = gid.y;\n let ch = gid.z;\n if (ox >= params.out_w || oy >= params.out_h || ch >= params.c) { return; }\n\n var best = -3.402823466e38;\n for (var yy = 0u; yy < params.ky; yy = yy + 1u) {\n let iy = i32(oy * params.sy + yy) - i32(params.py);\n if (iy < 0 || iy >= i32(params.h)) { continue; }\n for (var xx = 0u; xx < params.kx; xx = xx + 1u) {\n let ix = i32(ox * params.sx + xx) - i32(params.px);\n if (ix < 0 || ix >= i32(params.w)) { continue; }\n best = max(best, input[(u32(iy) * params.w + u32(ix)) * params.c + ch]);\n }\n }\n output[(oy * params.out_w + ox) * params.c + ch] = best;\n}\n";
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// shaders/interp1D.wgsl
|
|
425
|
+
var interp1D_default;
|
|
426
|
+
var init_interp1D = __esm({
|
|
427
|
+
"shaders/interp1D.wgsl"() {
|
|
428
|
+
interp1D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n \n struct Params { in_c : u32, in_l : u32, out_l : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let i = global_id.x;\n let c = global_id.y;\n \n if (i >= params.out_l || c >= params.in_c) { return; }\n \n let scale = f32(params.in_l) / f32(params.out_l);\n var src = (f32(i) + 0.5) * scale - 0.5;\n if (src < 0.0) { src = 0.0; }\n if (src > f32(params.in_l - 1u)) { src = f32(params.in_l - 1u); }\n \n let lo = u32(src);\n var hi = lo + 1u;\n if (hi >= params.in_l) { hi = params.in_l - 1u; }\n let t = src - f32(lo);\n \n let in_base = c * params.in_l;\n let out_base = c * params.out_l;\n \n output[out_base + i] = input[in_base + lo] * (1.0 - t) + input[in_base + hi] * t;\n }\n";
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// shaders/conv1D.wgsl
|
|
433
|
+
var conv1D_default;
|
|
434
|
+
var init_conv1D = __esm({
|
|
435
|
+
"shaders/conv1D.wgsl"() {
|
|
436
|
+
conv1D_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read> bias : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n \n struct Params {\n in_c : u32, in_l : u32,\n out_c : u32, k : u32,\n stride : u32, pad : u32, relu : u32\n }\n @group(0) @binding(4) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x;\n let oc = global_id.y;\n let out_l = (params.in_l + 2u * params.pad - params.k) / params.stride + 1u;\n \n if (x >= out_l || oc >= params.out_c) { return; }\n \n var sum = bias[oc];\n let w_base = oc * params.in_c * params.k;\n \n for (var ic = 0u; ic < params.in_c; ic = ic + 1u) {\n let in_base = ic * params.in_l;\n let w_ic_base = w_base + ic * params.k;\n for (var k = 0u; k < params.k; k = k + 1u) {\n let in_x = i32(x * params.stride + k) - i32(params.pad);\n if (in_x >= 0 && in_x < i32(params.in_l)) {\n sum = sum + input[in_base + u32(in_x)] * weight[w_ic_base + k];\n }\n }\n }\n \n if (params.relu == 1u && sum < 0.0) { sum = 0.0; }\n output[oc * out_l + x] = sum;\n }\n";
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// shaders/spatialSoftargmaxY.wgsl
|
|
441
|
+
var spatialSoftargmaxY_default;
|
|
442
|
+
var init_spatialSoftargmaxY = __esm({
|
|
443
|
+
"shaders/spatialSoftargmaxY.wgsl"() {
|
|
444
|
+
spatialSoftargmaxY_default = "@group(0) @binding(0) var<storage, read> input : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n \n struct Params { in_h : u32, in_w : u32, in_c : u32 }\n @group(0) @binding(2) var<uniform> params : Params;\n \n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let x = global_id.x;\n let c = global_id.y;\n \n if (x >= params.in_w || c >= params.in_c) { return; }\n \n var max_val = -1e38;\n for (var y = 0u; y < params.in_h; y = y + 1u) {\n let val = input[(y * params.in_w + x) * params.in_c + c];\n if (val > max_val) { max_val = val; }\n }\n \n var denom = 0.0;\n var weighted = 0.0;\n for (var y = 0u; y < params.in_h; y = y + 1u) {\n let val = input[(y * params.in_w + x) * params.in_c + c];\n let ev = exp(val - max_val);\n denom = denom + ev;\n weighted = weighted + ev * (f32(y) + 0.5) / f32(params.in_h);\n }\n \n var out_val = 0.0;\n if (denom > 0.0) { out_val = weighted / denom; }\n output[c * params.in_w + x] = out_val;\n }\n";
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// shaders/embedding.wgsl
|
|
449
|
+
var embedding_default;
|
|
450
|
+
var init_embedding = __esm({
|
|
451
|
+
"shaders/embedding.wgsl"() {
|
|
452
|
+
embedding_default = "@group(0) @binding(0) var<storage, read> tokens : array<f32>;\n @group(0) @binding(1) var<storage, read> weight : array<f32>;\n @group(0) @binding(2) var<storage, read_write> output : array<f32>;\n \n struct Params { seq_len : u32, d_model : u32 }\n @group(0) @binding(3) var<uniform> params : Params;\n\n @compute @workgroup_size(64)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let token_idx = global_id.x;\n if (token_idx >= params.seq_len) { return; }\n \n let d_model = params.d_model;\n let token_id = u32(tokens[token_idx]); // ids arrive as f32 (see CPU/WASM tiers)\n\n let in_offset = token_id * d_model;\n let out_offset = token_idx * d_model;\n \n for (var i = 0u; i < d_model; i = i + 1u) {\n output[out_offset + i] = weight[in_offset + i];\n }\n }\n";
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// shaders/sDPA.wgsl
|
|
457
|
+
var sDPA_default;
|
|
458
|
+
var init_sDPA = __esm({
|
|
459
|
+
"shaders/sDPA.wgsl"() {
|
|
460
|
+
sDPA_default = "@group(0) @binding(0) var<storage, read> qkv : array<f32>;\n @group(0) @binding(1) var<storage, read_write> output : array<f32>;\n \n struct Params {\n seq_len : u32,\n d_model : u32,\n num_heads : u32,\n head_dim : u32,\n scale : f32,\n }\n @group(0) @binding(2) var<uniform> params : Params;\n \n @compute @workgroup_size(64, 1, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let q_idx = global_id.x; \n let h_idx = global_id.y; \n \n if (q_idx >= params.seq_len || h_idx >= params.num_heads) { return; }\n \n let head_dim = params.head_dim;\n let d_model = params.d_model;\n \n var max_logit : f32 = -1e38;\n \n // Cache Q for this head and this q_idx\n var q_cache : array<f32, 64>; // max head_dim 64\n for (var d = 0u; d < head_dim; d = d + 1u) {\n q_cache[d] = qkv[q_idx * (d_model * 3u) + (h_idx * head_dim) + d];\n }\n \n // Pass 1: find max\n for (var k_idx = 0u; k_idx <= q_idx; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n let k_val = qkv[k_idx * (d_model * 3u) + d_model + (h_idx * head_dim) + d];\n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale;\n if (score > max_logit) { max_logit = score; }\n }\n \n // Pass 2: sum exp\n var sum_exp : f32 = 0.0;\n for (var k_idx = 0u; k_idx <= q_idx; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n let k_val = qkv[k_idx * (d_model * 3u) + d_model + (h_idx * head_dim) + d];\n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale;\n sum_exp = sum_exp + exp(score - max_logit);\n }\n \n // Pass 3: output\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var out_val : f32 = 0.0;\n for (var k_idx = 0u; k_idx <= q_idx; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var kd = 0u; kd < head_dim; kd = kd + 1u) {\n let k_val = qkv[k_idx * (d_model * 3u) + d_model + (h_idx * head_dim) + kd];\n score = score + (q_cache[kd] * k_val);\n }\n score = score * params.scale;\n \n let w = exp(score - max_logit) / sum_exp;\n let v_val = qkv[k_idx * (d_model * 3u) + d_model * 2u + (h_idx * head_dim) + d];\n out_val = out_val + (w * v_val);\n }\n output[q_idx * d_model + (h_idx * head_dim) + d] = out_val;\n }\n }\n";
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// shaders/crossSDPA.wgsl
|
|
465
|
+
var crossSDPA_default;
|
|
466
|
+
var init_crossSDPA = __esm({
|
|
467
|
+
"shaders/crossSDPA.wgsl"() {
|
|
468
|
+
crossSDPA_default = "@group(0) @binding(0) var<storage, read> q_in : array<f32>;\n @group(0) @binding(1) var<storage, read> k_in : array<f32>;\n @group(0) @binding(2) var<storage, read> v_in : array<f32>;\n @group(0) @binding(3) var<storage, read_write> output : array<f32>;\n \n struct Params {\n seq_len_q : u32,\n seq_len_kv : u32,\n d_model : u32,\n num_heads : u32,\n head_dim : u32,\n scale : f32,\n }\n @group(0) @binding(4) var<uniform> params : Params;\n \n @compute @workgroup_size(64, 1, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let q_idx = global_id.x; \n let h_idx = global_id.y; \n \n if (q_idx >= params.seq_len_q || h_idx >= params.num_heads) { return; }\n \n let head_dim = params.head_dim;\n let d_model = params.d_model;\n \n // Cache Q for this head and this q_idx\n var q_cache : array<f32, 64>; // max head_dim 64\n for (var d = 0u; d < head_dim; d = d + 1u) {\n q_cache[d] = q_in[q_idx * d_model + (h_idx * head_dim) + d];\n }\n \n var max_logit : f32 = -1e38;\n \n // Pass 1: find max\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n let k_val = k_in[k_idx * d_model + (h_idx * head_dim) + d];\n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale;\n if (score > max_logit) { max_logit = score; }\n }\n \n // Pass 2: sum exp\n var sum_exp : f32 = 0.0;\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n let k_val = k_in[k_idx * d_model + (h_idx * head_dim) + d];\n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale;\n sum_exp = sum_exp + exp(score - max_logit);\n }\n \n // Pass 3: output\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var out_val : f32 = 0.0;\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var kd = 0u; kd < head_dim; kd = kd + 1u) {\n let k_val = k_in[k_idx * d_model + (h_idx * head_dim) + kd];\n score = score + (q_cache[kd] * k_val);\n }\n score = score * params.scale;\n \n let w = exp(score - max_logit) / sum_exp;\n let v_val = v_in[k_idx * d_model + (h_idx * head_dim) + d];\n out_val = out_val + (w * v_val);\n }\n output[q_idx * d_model + (h_idx * head_dim) + d] = out_val;\n }\n }\n";
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// shaders/crossAttention.wgsl
|
|
473
|
+
var crossAttention_default;
|
|
474
|
+
var init_crossAttention = __esm({
|
|
475
|
+
"shaders/crossAttention.wgsl"() {
|
|
476
|
+
crossAttention_default = "@group(0) @binding(0) var<storage, read> q_in : array<f32>;\n @group(0) @binding(1) var<storage, read> kv_in : array<f32>;\n @group(0) @binding(2) var<storage, read> weight_int8_packed : array<u32>;\n @group(0) @binding(3) var<storage, read> w_scale : array<f32>;\n @group(0) @binding(4) var<storage, read> bias : array<f32>;\n @group(0) @binding(5) var<storage, read_write> output : array<f32>;\n \n struct Params {\n seq_len_q : u32,\n seq_len_kv : u32,\n d_model : u32,\n num_heads : u32,\n head_dim : u32,\n scale_factor : f32,\n has_scale : u32,\n has_bias : u32,\n }\n @group(0) @binding(6) var<uniform> params : Params;\n \n @compute @workgroup_size(64, 1, 1)\n fn main(@builtin(global_invocation_id) global_id : vec3<u32>) {\n let q_idx = global_id.x; \n let h_idx = global_id.y; \n \n if (q_idx >= params.seq_len_q || h_idx >= params.num_heads) { return; }\n \n let head_dim = params.head_dim;\n let d_model = params.d_model;\n let d_model_4 = d_model / 4u;\n \n // Cache Q for this head and this q_idx\n var q_cache : array<f32, 64>; // max head_dim 64\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var sum = 0.0;\n let out_col = h_idx * head_dim + d;\n for (var i = 0u; i < d_model_4; i = i + 1u) {\n let w_packed = weight_int8_packed[out_col * d_model_4 + i];\n let in_base = q_idx * d_model + i * 4u;\n sum = sum + q_in[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n sum = sum + q_in[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n sum = sum + q_in[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n sum = sum + q_in[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n if (params.has_scale > 0u) { sum = sum * w_scale[out_col]; }\n if (params.has_bias > 0u) { sum = sum + bias[out_col]; }\n q_cache[d] = sum;\n }\n \n var max_logit : f32 = -1e38;\n \n // Pass 1: find max\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var k_val = 0.0;\n let out_col = d_model + h_idx * head_dim + d;\n for (var i = 0u; i < d_model_4; i = i + 1u) {\n let w_packed = weight_int8_packed[out_col * d_model_4 + i];\n let in_base = k_idx * d_model + i * 4u;\n k_val = k_val + kv_in[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n k_val = k_val + kv_in[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n k_val = k_val + kv_in[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n k_val = k_val + kv_in[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n if (params.has_scale > 0u) { k_val = k_val * w_scale[out_col]; }\n if (params.has_bias > 0u) { k_val = k_val + bias[out_col]; }\n \n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale_factor;\n if (score > max_logit) { max_logit = score; }\n }\n \n // Pass 2: sum exp\n var sum_exp : f32 = 0.0;\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var k_val = 0.0;\n let out_col = d_model + h_idx * head_dim + d;\n for (var i = 0u; i < d_model_4; i = i + 1u) {\n let w_packed = weight_int8_packed[out_col * d_model_4 + i];\n let in_base = k_idx * d_model + i * 4u;\n k_val = k_val + kv_in[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n k_val = k_val + kv_in[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n k_val = k_val + kv_in[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n k_val = k_val + kv_in[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n if (params.has_scale > 0u) { k_val = k_val * w_scale[out_col]; }\n if (params.has_bias > 0u) { k_val = k_val + bias[out_col]; }\n \n score = score + (q_cache[d] * k_val);\n }\n score = score * params.scale_factor;\n sum_exp = sum_exp + exp(score - max_logit);\n }\n \n // Pass 3: output\n for (var d = 0u; d < head_dim; d = d + 1u) {\n var out_val : f32 = 0.0;\n for (var k_idx = 0u; k_idx < params.seq_len_kv; k_idx = k_idx + 1u) {\n var score : f32 = 0.0;\n for (var kd = 0u; kd < head_dim; kd = kd + 1u) {\n var k_val = 0.0;\n let out_col = d_model + h_idx * head_dim + kd;\n for (var i = 0u; i < d_model_4; i = i + 1u) {\n let w_packed = weight_int8_packed[out_col * d_model_4 + i];\n let in_base = k_idx * d_model + i * 4u;\n k_val = k_val + kv_in[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n k_val = k_val + kv_in[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n k_val = k_val + kv_in[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n k_val = k_val + kv_in[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n if (params.has_scale > 0u) { k_val = k_val * w_scale[out_col]; }\n if (params.has_bias > 0u) { k_val = k_val + bias[out_col]; }\n \n score = score + (q_cache[kd] * k_val);\n }\n score = score * params.scale_factor;\n \n let w = exp(score - max_logit) / sum_exp;\n \n var v_val = 0.0;\n let v_col = d_model * 2u + h_idx * head_dim + d;\n for (var i = 0u; i < d_model_4; i = i + 1u) {\n let w_packed = weight_int8_packed[v_col * d_model_4 + i];\n let in_base = k_idx * d_model + i * 4u;\n v_val = v_val + kv_in[in_base + 0u] * f32(extractBits(i32(w_packed), 0u, 8u));\n v_val = v_val + kv_in[in_base + 1u] * f32(extractBits(i32(w_packed), 8u, 8u));\n v_val = v_val + kv_in[in_base + 2u] * f32(extractBits(i32(w_packed), 16u, 8u));\n v_val = v_val + kv_in[in_base + 3u] * f32(extractBits(i32(w_packed), 24u, 8u));\n }\n if (params.has_scale > 0u) { v_val = v_val * w_scale[v_col]; }\n if (params.has_bias > 0u) { v_val = v_val + bias[v_col]; }\n \n out_val = out_val + (w * v_val);\n }\n output[q_idx * d_model + (h_idx * head_dim) + d] = out_val;\n }\n }\n";
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// js/ShaderLibrary.js
|
|
481
|
+
var ShaderLibrary_exports = {};
|
|
482
|
+
__export(ShaderLibrary_exports, {
|
|
483
|
+
ShaderLibrary: () => ShaderLibrary
|
|
484
|
+
});
|
|
485
|
+
var ShaderLibrary;
|
|
486
|
+
var init_ShaderLibrary = __esm({
|
|
487
|
+
"js/ShaderLibrary.js"() {
|
|
488
|
+
init_linearF32();
|
|
489
|
+
init_linearInt8();
|
|
490
|
+
init_conv2D();
|
|
491
|
+
init_conv2DDepthwise8();
|
|
492
|
+
init_conv2DPointwise16();
|
|
493
|
+
init_conv2DPointwise16Tile();
|
|
494
|
+
init_conv2DPointwise8Vec2();
|
|
495
|
+
init_conv2DPointwise8Vec4();
|
|
496
|
+
init_conv2DRegularC3Out16();
|
|
497
|
+
init_layerNorm();
|
|
498
|
+
init_binaryBroadcast();
|
|
499
|
+
init_elementwise();
|
|
500
|
+
init_resize();
|
|
501
|
+
init_slice();
|
|
502
|
+
init_sub();
|
|
503
|
+
init_div();
|
|
504
|
+
init_siLU();
|
|
505
|
+
init_leakyReLU();
|
|
506
|
+
init_tanh();
|
|
507
|
+
init_clip();
|
|
508
|
+
init_rMSNorm();
|
|
509
|
+
init_softmax();
|
|
510
|
+
init_pReLU();
|
|
511
|
+
init_logSoftmax();
|
|
512
|
+
init_reduce();
|
|
513
|
+
init_averagePool2D();
|
|
514
|
+
init_gather();
|
|
515
|
+
init_where();
|
|
516
|
+
init_dequantizeLinear();
|
|
517
|
+
init_expand();
|
|
518
|
+
init_pad();
|
|
519
|
+
init_convTranspose2D();
|
|
520
|
+
init_reLU();
|
|
521
|
+
init_sigmoid();
|
|
522
|
+
init_hardSwish();
|
|
523
|
+
init_hardSigmoid();
|
|
524
|
+
init_copy();
|
|
525
|
+
init_batchNorm2D();
|
|
526
|
+
init_gELU();
|
|
527
|
+
init_add();
|
|
528
|
+
init_upsample2x();
|
|
529
|
+
init_concatCopy();
|
|
530
|
+
init_concat2();
|
|
531
|
+
init_broadcastBinary();
|
|
532
|
+
init_generalTranspose();
|
|
533
|
+
init_split();
|
|
534
|
+
init_profileY();
|
|
535
|
+
init_profileX();
|
|
536
|
+
init_globalAveragePool();
|
|
537
|
+
init_meanHeight();
|
|
538
|
+
init_maxPool2D();
|
|
539
|
+
init_interp1D();
|
|
540
|
+
init_conv1D();
|
|
541
|
+
init_spatialSoftargmaxY();
|
|
542
|
+
init_embedding();
|
|
543
|
+
init_sDPA();
|
|
544
|
+
init_crossSDPA();
|
|
545
|
+
init_crossAttention();
|
|
546
|
+
ShaderLibrary = class {
|
|
547
|
+
static getLinearF32Shader() {
|
|
548
|
+
return linearF32_default;
|
|
549
|
+
}
|
|
550
|
+
static getLinearInt8Shader() {
|
|
551
|
+
return linearInt8_default;
|
|
552
|
+
}
|
|
553
|
+
static getConv2DShader() {
|
|
554
|
+
return conv2D_default;
|
|
555
|
+
}
|
|
556
|
+
static getConv2DDepthwise8Shader() {
|
|
557
|
+
return conv2DDepthwise8_default;
|
|
558
|
+
}
|
|
559
|
+
static getConv2DPointwise16Shader() {
|
|
560
|
+
return conv2DPointwise16_default;
|
|
561
|
+
}
|
|
562
|
+
static getConv2DPointwise16TileShader() {
|
|
563
|
+
return conv2DPointwise16Tile_default;
|
|
564
|
+
}
|
|
565
|
+
static getConv2DPointwise8Vec2Shader() {
|
|
566
|
+
return conv2DPointwise8Vec2_default;
|
|
567
|
+
}
|
|
568
|
+
static getConv2DPointwise8Vec4Shader() {
|
|
569
|
+
return conv2DPointwise8Vec4_default;
|
|
570
|
+
}
|
|
571
|
+
static getConv2DRegularC3Out16Shader() {
|
|
572
|
+
return conv2DRegularC3Out16_default;
|
|
573
|
+
}
|
|
574
|
+
static getLayerNormShader() {
|
|
575
|
+
return layerNorm_default;
|
|
576
|
+
}
|
|
577
|
+
static getBinaryBroadcastShader() {
|
|
578
|
+
return binaryBroadcast_default;
|
|
579
|
+
}
|
|
580
|
+
static getElementwiseShader() {
|
|
581
|
+
return elementwise_default;
|
|
582
|
+
}
|
|
583
|
+
static getResizeShader() {
|
|
584
|
+
return resize_default;
|
|
585
|
+
}
|
|
586
|
+
static getSliceShader() {
|
|
587
|
+
return slice_default;
|
|
588
|
+
}
|
|
589
|
+
static getSubShader() {
|
|
590
|
+
return sub_default;
|
|
591
|
+
}
|
|
592
|
+
static getDivShader() {
|
|
593
|
+
return div_default;
|
|
594
|
+
}
|
|
595
|
+
static getSiLUShader() {
|
|
596
|
+
return siLU_default;
|
|
597
|
+
}
|
|
598
|
+
static getLeakyReLUShader() {
|
|
599
|
+
return leakyReLU_default;
|
|
600
|
+
}
|
|
601
|
+
static getTanhShader() {
|
|
602
|
+
return tanh_default;
|
|
603
|
+
}
|
|
604
|
+
static getClipShader() {
|
|
605
|
+
return clip_default;
|
|
606
|
+
}
|
|
607
|
+
static getRMSNormShader() {
|
|
608
|
+
return rMSNorm_default;
|
|
609
|
+
}
|
|
610
|
+
static getSoftmaxShader() {
|
|
611
|
+
return softmax_default;
|
|
612
|
+
}
|
|
613
|
+
static getPReLUShader() {
|
|
614
|
+
return pReLU_default;
|
|
615
|
+
}
|
|
616
|
+
static getLogSoftmaxShader() {
|
|
617
|
+
return logSoftmax_default;
|
|
618
|
+
}
|
|
619
|
+
static getReduceShader() {
|
|
620
|
+
return reduce_default;
|
|
621
|
+
}
|
|
622
|
+
static getAveragePool2DShader() {
|
|
623
|
+
return averagePool2D_default;
|
|
624
|
+
}
|
|
625
|
+
static getGatherShader() {
|
|
626
|
+
return gather_default;
|
|
627
|
+
}
|
|
628
|
+
static getWhereShader() {
|
|
629
|
+
return where_default;
|
|
630
|
+
}
|
|
631
|
+
static getDequantizeLinearShader() {
|
|
632
|
+
return dequantizeLinear_default;
|
|
633
|
+
}
|
|
634
|
+
static getExpandShader() {
|
|
635
|
+
return expand_default;
|
|
636
|
+
}
|
|
637
|
+
static getPadShader() {
|
|
638
|
+
return pad_default;
|
|
639
|
+
}
|
|
640
|
+
static getConvTranspose2DShader() {
|
|
641
|
+
return convTranspose2D_default;
|
|
642
|
+
}
|
|
643
|
+
static getReLUShader() {
|
|
644
|
+
return reLU_default;
|
|
645
|
+
}
|
|
646
|
+
static getSigmoidShader() {
|
|
647
|
+
return sigmoid_default;
|
|
648
|
+
}
|
|
649
|
+
static getHardSwishShader() {
|
|
650
|
+
return hardSwish_default;
|
|
651
|
+
}
|
|
652
|
+
static getHardSigmoidShader() {
|
|
653
|
+
return hardSigmoid_default;
|
|
654
|
+
}
|
|
655
|
+
static getCopyShader() {
|
|
656
|
+
return copy_default;
|
|
657
|
+
}
|
|
658
|
+
static getBatchNorm2DShader() {
|
|
659
|
+
return batchNorm2D_default;
|
|
660
|
+
}
|
|
661
|
+
static getGELUShader() {
|
|
662
|
+
return gELU_default;
|
|
663
|
+
}
|
|
664
|
+
static getAddShader() {
|
|
665
|
+
return add_default;
|
|
666
|
+
}
|
|
667
|
+
static getUpsample2xShader() {
|
|
668
|
+
return upsample2x_default;
|
|
669
|
+
}
|
|
670
|
+
static getConcatCopyShader() {
|
|
671
|
+
return concatCopy_default;
|
|
672
|
+
}
|
|
673
|
+
static getConcat2Shader() {
|
|
674
|
+
return concat2_default;
|
|
675
|
+
}
|
|
676
|
+
static getBroadcastBinaryShader(binOp = "out_val = av + bv;") {
|
|
677
|
+
return broadcastBinary_default.replace("//__BINOP__", binOp);
|
|
678
|
+
}
|
|
679
|
+
static getGeneralTransposeShader() {
|
|
680
|
+
return generalTranspose_default;
|
|
681
|
+
}
|
|
682
|
+
static getSplitShader() {
|
|
683
|
+
return split_default;
|
|
684
|
+
}
|
|
685
|
+
static getProfileYShader() {
|
|
686
|
+
return profileY_default;
|
|
687
|
+
}
|
|
688
|
+
static getProfileXShader() {
|
|
689
|
+
return profileX_default;
|
|
690
|
+
}
|
|
691
|
+
static getGlobalAveragePoolShader() {
|
|
692
|
+
return globalAveragePool_default;
|
|
693
|
+
}
|
|
694
|
+
static getMeanHeightShader() {
|
|
695
|
+
return meanHeight_default;
|
|
696
|
+
}
|
|
697
|
+
static getMaxPool2DShader() {
|
|
698
|
+
return maxPool2D_default;
|
|
699
|
+
}
|
|
700
|
+
static getInterp1DShader() {
|
|
701
|
+
return interp1D_default;
|
|
702
|
+
}
|
|
703
|
+
static getConv1DShader() {
|
|
704
|
+
return conv1D_default;
|
|
705
|
+
}
|
|
706
|
+
static getSpatialSoftargmaxYShader() {
|
|
707
|
+
return spatialSoftargmaxY_default;
|
|
708
|
+
}
|
|
709
|
+
static getEmbeddingShader() {
|
|
710
|
+
return embedding_default;
|
|
711
|
+
}
|
|
712
|
+
static getSDPAShader() {
|
|
713
|
+
return sDPA_default;
|
|
714
|
+
}
|
|
715
|
+
static getCrossSDPAShader() {
|
|
716
|
+
return crossSDPA_default;
|
|
717
|
+
}
|
|
718
|
+
static getCrossAttentionShader() {
|
|
719
|
+
return crossAttention_default;
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
// js/Tensor.js
|
|
726
|
+
var Tensor = class {
|
|
727
|
+
constructor(name, shape, dtype = "float32", isWeight = false) {
|
|
728
|
+
this.name = name;
|
|
729
|
+
this.shape = shape;
|
|
730
|
+
this.dtype = dtype;
|
|
731
|
+
this.isWeight = isWeight;
|
|
732
|
+
this.gpuBuffer = null;
|
|
733
|
+
this.sizeBytes = this._calculateByteSize();
|
|
734
|
+
}
|
|
735
|
+
_calculateByteSize() {
|
|
736
|
+
const elements = this.shape.reduce((a, b) => a * b, 1);
|
|
737
|
+
if (this.dtype === "float32" || this.dtype === "int32") return elements * 4;
|
|
738
|
+
if (this.dtype === "int8" || this.dtype === "uint8") return elements;
|
|
739
|
+
return elements * 4;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
function _pair(v, def) {
|
|
743
|
+
if (Array.isArray(v)) return [v[0], v[1] ?? v[0]];
|
|
744
|
+
const s = v ?? def;
|
|
745
|
+
return [s, s];
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// js/Graph.js
|
|
749
|
+
var Graph = class {
|
|
750
|
+
constructor() {
|
|
751
|
+
this.nodes = [];
|
|
752
|
+
this.tensors = /* @__PURE__ */ new Map();
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Define an input tensor
|
|
756
|
+
*/
|
|
757
|
+
addInput(name, shape, dtype = "float32") {
|
|
758
|
+
const tensor = new Tensor(name, shape, dtype, false);
|
|
759
|
+
this.tensors.set(name, tensor);
|
|
760
|
+
return tensor;
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Define a weight tensor (learned parameter)
|
|
764
|
+
*/
|
|
765
|
+
addWeight(name, shape, dtype = "float32") {
|
|
766
|
+
const tensor = new Tensor(name, shape, dtype, true);
|
|
767
|
+
this.tensors.set(name, tensor);
|
|
768
|
+
return tensor;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Add a computation operation to the graph
|
|
772
|
+
* @param {string} opType - e.g., 'MatMul', 'Conv2D', 'LayerNorm'
|
|
773
|
+
* @param {Object} inputs - Key-value pair of input names to Tensor objects
|
|
774
|
+
* @param {Object} outputs - Key-value pair of output names to Tensor shapes
|
|
775
|
+
* @param {Object} params - Uniform parameters for the shader (e.g., stride, kernel size)
|
|
776
|
+
*/
|
|
777
|
+
addOp(opType, inputs, outputs, params = {}) {
|
|
778
|
+
const outTensors = {};
|
|
779
|
+
for (const [key, shape] of Object.entries(outputs)) {
|
|
780
|
+
const outName = `${opType}_${this.nodes.length}_out_${key}`;
|
|
781
|
+
const t = new Tensor(outName, shape, "float32", false);
|
|
782
|
+
this.tensors.set(outName, t);
|
|
783
|
+
outTensors[key] = t;
|
|
784
|
+
}
|
|
785
|
+
const node = {
|
|
786
|
+
id: this.nodes.length,
|
|
787
|
+
opType,
|
|
788
|
+
inputs,
|
|
789
|
+
outputs: outTensors,
|
|
790
|
+
params
|
|
791
|
+
};
|
|
792
|
+
this.nodes.push(node);
|
|
793
|
+
return outTensors;
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
// js/ops/pReLU.js
|
|
798
|
+
function _cpuPReLU(node) {
|
|
799
|
+
const inBuf = (node.inputs.input || node.inputs.x).buffer;
|
|
800
|
+
const slope = (node.inputs.slope || node.inputs.weight).buffer;
|
|
801
|
+
const outBuf = node.outputs.out.buffer;
|
|
802
|
+
const shape = (node.inputs.input || node.inputs.x).shape || [];
|
|
803
|
+
const channels = shape.length === 4 ? shape[3] : slope.length;
|
|
804
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
805
|
+
const alpha = slope.length === channels ? slope[i % channels] : slope[i % slope.length];
|
|
806
|
+
outBuf[i] = inBuf[i] < 0 ? inBuf[i] * alpha : inBuf[i];
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// js/ops/expand.js
|
|
811
|
+
function _cpuExpand(node) {
|
|
812
|
+
const inBuf = node.inputs.input.buffer;
|
|
813
|
+
const outBuf = node.outputs.out.buffer;
|
|
814
|
+
if (inBuf.length === 1) {
|
|
815
|
+
for (let i = 0; i < outBuf.length; i++) outBuf[i] = inBuf[0];
|
|
816
|
+
} else if (inBuf.length === outBuf.length) {
|
|
817
|
+
outBuf.set(inBuf);
|
|
818
|
+
} else {
|
|
819
|
+
const pad4 = (sh) => [1, 1, 1, 1].slice(0, 4 - sh.length).concat(sh);
|
|
820
|
+
const [ib, ih, iw, ic] = pad4(node.inputs.input.shape);
|
|
821
|
+
const [ob, oh, ow, oc] = pad4(node.outputs.out.shape);
|
|
822
|
+
for (let b = 0; b < ob; b++) {
|
|
823
|
+
for (let y = 0; y < oh; y++) {
|
|
824
|
+
for (let x = 0; x < ow; x++) {
|
|
825
|
+
for (let c = 0; c < oc; c++) {
|
|
826
|
+
const src = ((b % ib * ih + y % ih) * iw + x % iw) * ic + c % ic;
|
|
827
|
+
const dst = ((b * oh + y) * ow + x) * oc + c;
|
|
828
|
+
outBuf[dst] = inBuf[src];
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// js/ops/dequantizeLinear.js
|
|
837
|
+
function _cpuDequantizeLinear(node) {
|
|
838
|
+
const inBuf = node.inputs.input.buffer;
|
|
839
|
+
const scale = node.inputs.scale.buffer[0];
|
|
840
|
+
const zp = node.inputs.zero_point ? node.inputs.zero_point.buffer[0] : 0;
|
|
841
|
+
const outBuf = node.outputs.out.buffer;
|
|
842
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
843
|
+
outBuf[i] = (inBuf[i] - zp) * scale;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// js/ops/tanh.js
|
|
848
|
+
function _cpuTanh(node) {
|
|
849
|
+
const inBuf = node.inputs.input.buffer;
|
|
850
|
+
const outBuf = node.outputs.out.buffer;
|
|
851
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
852
|
+
outBuf[i] = Math.tanh(inBuf[i]);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// js/ops/rMSNorm.js
|
|
857
|
+
function _cpuRMSNorm(node) {
|
|
858
|
+
const input = node.inputs.input || node.inputs.x;
|
|
859
|
+
const weight = node.inputs.weight;
|
|
860
|
+
const outBuf = node.outputs.out.buffer;
|
|
861
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
862
|
+
const b = in_shape[0], d = in_shape[1];
|
|
863
|
+
const eps = node.params.eps || 1e-5;
|
|
864
|
+
for (let i = 0; i < b; i++) {
|
|
865
|
+
let sq_sum = 0;
|
|
866
|
+
for (let j = 0; j < d; j++) sq_sum += input.buffer[i * d + j] * input.buffer[i * d + j];
|
|
867
|
+
const rms = Math.sqrt(sq_sum / d + eps);
|
|
868
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] = input.buffer[i * d + j] / rms * weight.buffer[j];
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// js/ops/siLU.js
|
|
873
|
+
function _cpuSiLU(node) {
|
|
874
|
+
const inBuf = node.inputs.input.buffer;
|
|
875
|
+
const outBuf = node.outputs.out.buffer;
|
|
876
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
877
|
+
const x = inBuf[i];
|
|
878
|
+
outBuf[i] = x / (1 + Math.exp(-x));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// js/ops/sub.js
|
|
883
|
+
function _cpuSub(node) {
|
|
884
|
+
const aBuf = node.inputs.a.buffer;
|
|
885
|
+
const bBuf = node.inputs.b.buffer;
|
|
886
|
+
const outBuf = node.outputs.out.buffer;
|
|
887
|
+
const elements = outBuf.length;
|
|
888
|
+
if (bBuf.length === 1) {
|
|
889
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] - bBuf[0];
|
|
890
|
+
} else {
|
|
891
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] - bBuf[i];
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// js/ops/logSoftmax.js
|
|
896
|
+
function _cpuLogSoftmax(node) {
|
|
897
|
+
const input = node.inputs.input || node.inputs.x;
|
|
898
|
+
const outBuf = node.outputs.out.buffer;
|
|
899
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
900
|
+
const b = in_shape[0];
|
|
901
|
+
const d = in_shape[1];
|
|
902
|
+
for (let i = 0; i < b; i++) {
|
|
903
|
+
let max = -Infinity;
|
|
904
|
+
for (let j = 0; j < d; j++) max = Math.max(max, input.buffer[i * d + j]);
|
|
905
|
+
let sum = 0;
|
|
906
|
+
for (let j = 0; j < d; j++) sum += Math.exp(input.buffer[i * d + j] - max);
|
|
907
|
+
const logSum = Math.log(sum);
|
|
908
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] = input.buffer[i * d + j] - max - logSum;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// js/ops/softmax.js
|
|
913
|
+
function _cpuSoftmax(node) {
|
|
914
|
+
const input = node.inputs.input || node.inputs.x;
|
|
915
|
+
const outBuf = node.outputs.out.buffer;
|
|
916
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
917
|
+
const b = in_shape[0];
|
|
918
|
+
const d = in_shape[1];
|
|
919
|
+
for (let i = 0; i < b; i++) {
|
|
920
|
+
let max = -Infinity;
|
|
921
|
+
for (let j = 0; j < d; j++) max = Math.max(max, input.buffer[i * d + j]);
|
|
922
|
+
let sum = 0;
|
|
923
|
+
for (let j = 0; j < d; j++) {
|
|
924
|
+
const val = Math.exp(input.buffer[i * d + j] - max);
|
|
925
|
+
outBuf[i * d + j] = val;
|
|
926
|
+
sum += val;
|
|
927
|
+
}
|
|
928
|
+
for (let j = 0; j < d; j++) outBuf[i * d + j] /= sum;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// js/ops/where.js
|
|
933
|
+
function _cpuWhere(node) {
|
|
934
|
+
const cond = node.inputs.cond || node.inputs.condition;
|
|
935
|
+
const a = node.inputs.x || node.inputs.a;
|
|
936
|
+
const b = node.inputs.y || node.inputs.b;
|
|
937
|
+
const outBuf = node.outputs.out.buffer;
|
|
938
|
+
const condBuf = cond.buffer;
|
|
939
|
+
const aBuf = a.buffer;
|
|
940
|
+
const bBuf = b.buffer;
|
|
941
|
+
const elements = outBuf.length;
|
|
942
|
+
for (let i = 0; i < elements; i++) {
|
|
943
|
+
outBuf[i] = condBuf[i] !== 0 ? aBuf[i] : bBuf[i];
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// js/ops/pad.js
|
|
948
|
+
function _cpuPad(node) {
|
|
949
|
+
const input = node.inputs.input || node.inputs.data;
|
|
950
|
+
const outBuf = node.outputs.out.buffer;
|
|
951
|
+
const pads = node.params.pads || [];
|
|
952
|
+
const val = node.params.value || 0;
|
|
953
|
+
let pt = 0, pb = 0, pl = 0, pr = 0;
|
|
954
|
+
if (pads.length === 8) {
|
|
955
|
+
pt = pads[1];
|
|
956
|
+
pl = pads[2];
|
|
957
|
+
pb = pads[5];
|
|
958
|
+
pr = pads[6];
|
|
959
|
+
} else if (pads.length === 4) {
|
|
960
|
+
pt = pads[0];
|
|
961
|
+
pl = pads[1];
|
|
962
|
+
pb = pads[2];
|
|
963
|
+
pr = pads[3];
|
|
964
|
+
}
|
|
965
|
+
const inShape = input.shape.length === 4 ? input.shape : [1, input.shape[0] || 1, input.shape[1] || 1, 1];
|
|
966
|
+
const [b, in_h, in_w, c] = inShape;
|
|
967
|
+
const out_h = in_h + pt + pb;
|
|
968
|
+
const out_w = in_w + pl + pr;
|
|
969
|
+
for (let i = 0; i < outBuf.length; i++) outBuf[i] = val;
|
|
970
|
+
for (let batch = 0; batch < b; batch++) {
|
|
971
|
+
for (let y = 0; y < in_h; y++) {
|
|
972
|
+
for (let x = 0; x < in_w; x++) {
|
|
973
|
+
for (let chan = 0; chan < c; chan++) {
|
|
974
|
+
const inIdx = ((batch * in_h + y) * in_w + x) * c + chan;
|
|
975
|
+
const outIdx = ((batch * out_h + (y + pt)) * out_w + (x + pl)) * c + chan;
|
|
976
|
+
outBuf[outIdx] = input.buffer[inIdx];
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// js/ops/averagePool2D.js
|
|
984
|
+
function _cpuAveragePool2D(node) {
|
|
985
|
+
const input = node.inputs.input || node.inputs.x;
|
|
986
|
+
const inBuf = input.buffer;
|
|
987
|
+
const outBuf = node.outputs.out.buffer;
|
|
988
|
+
const [b, in_h, in_w, c] = input.shape;
|
|
989
|
+
const [out_b, out_h, out_w, out_c] = node.outputs.out.shape;
|
|
990
|
+
const kh = node.params.kernel[0], kw = node.params.kernel[1];
|
|
991
|
+
const sh = node.params.stride ? node.params.stride[0] : 1;
|
|
992
|
+
const sw = node.params.stride ? node.params.stride[1] : 1;
|
|
993
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
994
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
995
|
+
for (let batch = 0; batch < b; batch++) {
|
|
996
|
+
for (let y = 0; y < out_h; y++) {
|
|
997
|
+
for (let x = 0; x < out_w; x++) {
|
|
998
|
+
for (let chan = 0; chan < c; chan++) {
|
|
999
|
+
let sum = 0;
|
|
1000
|
+
let count = 0;
|
|
1001
|
+
for (let ky = 0; ky < kh; ky++) {
|
|
1002
|
+
for (let kx = 0; kx < kw; kx++) {
|
|
1003
|
+
const in_y = y * sh - ph + ky;
|
|
1004
|
+
const in_x = x * sw - pw + kx;
|
|
1005
|
+
if (in_y >= 0 && in_y < in_h && in_x >= 0 && in_x < in_w) {
|
|
1006
|
+
const inIdx = ((batch * in_h + in_y) * in_w + in_x) * c + chan;
|
|
1007
|
+
sum += inBuf[inIdx];
|
|
1008
|
+
count++;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const outIdx = ((batch * out_h + y) * out_w + x) * out_c + chan;
|
|
1013
|
+
outBuf[outIdx] = count > 0 ? sum / count : 0;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// js/ops/slice.js
|
|
1021
|
+
function _cpuSlice(node) {
|
|
1022
|
+
const input = node.inputs.input || node.inputs.data;
|
|
1023
|
+
const outBuf = node.outputs.out.buffer;
|
|
1024
|
+
const starts = node.params.starts || [0, 0, 0, 0];
|
|
1025
|
+
const steps = node.params.steps || [1, 1, 1, 1];
|
|
1026
|
+
const axes = node.params.axes || [0, 1, 2, 3];
|
|
1027
|
+
const in_s = [1, 1, 1, 1].slice(0, 4 - input.shape.length).concat(input.shape);
|
|
1028
|
+
const out_s = [1, 1, 1, 1].slice(0, 4 - node.outputs.out.shape.length).concat(node.outputs.out.shape);
|
|
1029
|
+
const st = [0, 0, 0, 0];
|
|
1030
|
+
const sp = [1, 1, 1, 1];
|
|
1031
|
+
for (let i = 0; i < axes.length; i++) {
|
|
1032
|
+
let ax = axes[i];
|
|
1033
|
+
if (ax < 0) ax += input.shape.length;
|
|
1034
|
+
ax += 4 - input.shape.length;
|
|
1035
|
+
st[ax] = starts[i] < 0 ? starts[i] + in_s[ax] : starts[i];
|
|
1036
|
+
sp[ax] = steps[i];
|
|
1037
|
+
}
|
|
1038
|
+
let outIdx = 0;
|
|
1039
|
+
for (let i0 = 0; i0 < out_s[0]; i0++) {
|
|
1040
|
+
for (let i1 = 0; i1 < out_s[1]; i1++) {
|
|
1041
|
+
for (let i2 = 0; i2 < out_s[2]; i2++) {
|
|
1042
|
+
for (let i3 = 0; i3 < out_s[3]; i3++) {
|
|
1043
|
+
const src0 = st[0] + i0 * sp[0];
|
|
1044
|
+
const src1 = st[1] + i1 * sp[1];
|
|
1045
|
+
const src2 = st[2] + i2 * sp[2];
|
|
1046
|
+
const src3 = st[3] + i3 * sp[3];
|
|
1047
|
+
const inIdx = src0 * (in_s[1] * in_s[2] * in_s[3]) + src1 * (in_s[2] * in_s[3]) + src2 * in_s[3] + src3;
|
|
1048
|
+
outBuf[outIdx++] = input.buffer[inIdx];
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// js/ops/convTranspose2D.js
|
|
1056
|
+
function _cpuConvTranspose2D(node) {
|
|
1057
|
+
const input = node.inputs.input || node.inputs.x;
|
|
1058
|
+
const weight = node.inputs.weight;
|
|
1059
|
+
const bias = node.inputs.bias;
|
|
1060
|
+
const inBuf = input.buffer;
|
|
1061
|
+
const wBuf = weight.buffer;
|
|
1062
|
+
const bBuf = bias ? bias.buffer : null;
|
|
1063
|
+
const outBuf = node.outputs.out.buffer;
|
|
1064
|
+
const [b, in_h, in_w, in_c] = input.shape;
|
|
1065
|
+
const [out_b, out_h, out_w, out_c] = node.outputs.out.shape;
|
|
1066
|
+
const kh = node.params.kernel[0], kw = node.params.kernel[1];
|
|
1067
|
+
const sh = node.params.stride ? node.params.stride[0] : 1;
|
|
1068
|
+
const sw = node.params.stride ? node.params.stride[1] : 1;
|
|
1069
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
1070
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
1071
|
+
for (let i = 0; i < outBuf.length; i++) {
|
|
1072
|
+
outBuf[i] = bBuf ? bBuf[i % out_c] : 0;
|
|
1073
|
+
}
|
|
1074
|
+
for (let i_b = 0; i_b < b; i_b++) {
|
|
1075
|
+
for (let i_ic = 0; i_ic < in_c; i_ic++) {
|
|
1076
|
+
for (let iy = 0; iy < in_h; iy++) {
|
|
1077
|
+
for (let ix = 0; ix < in_w; ix++) {
|
|
1078
|
+
const in_val = inBuf[((i_b * in_h + iy) * in_w + ix) * in_c + i_ic];
|
|
1079
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
1080
|
+
for (let ky = 0; ky < kh; ky++) {
|
|
1081
|
+
for (let kx = 0; kx < kw; kx++) {
|
|
1082
|
+
const oy = iy * sh - ph + ky;
|
|
1083
|
+
const ox = ix * sw - pw + kx;
|
|
1084
|
+
if (oy >= 0 && oy < out_h && ox >= 0 && ox < out_w) {
|
|
1085
|
+
const w_val = wBuf[i_ic * (out_c * kh * kw) + oc * (kh * kw) + ky * kw + kx];
|
|
1086
|
+
outBuf[((i_b * out_h + oy) * out_w + ox) * out_c + oc] += in_val * w_val;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// js/ops/reduceSum.js
|
|
1098
|
+
function _cpuReduceSum(node) {
|
|
1099
|
+
const input = node.inputs.input || node.inputs.data;
|
|
1100
|
+
const inBuf = input.buffer;
|
|
1101
|
+
const outBuf = node.outputs.out.buffer;
|
|
1102
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
1103
|
+
const b = in_shape[0];
|
|
1104
|
+
const d = in_shape[1];
|
|
1105
|
+
for (let i = 0; i < b; i++) {
|
|
1106
|
+
let sum = 0;
|
|
1107
|
+
for (let j = 0; j < d; j++) {
|
|
1108
|
+
sum += inBuf[i * d + j];
|
|
1109
|
+
}
|
|
1110
|
+
outBuf[i] = sum;
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// js/ops/reduceMean.js
|
|
1115
|
+
function _cpuReduceMean(node) {
|
|
1116
|
+
const input = node.inputs.input || node.inputs.data;
|
|
1117
|
+
const inBuf = input.buffer;
|
|
1118
|
+
const outBuf = node.outputs.out.buffer;
|
|
1119
|
+
const in_shape = input.shape.length === 2 ? input.shape : [1, input.buffer.length];
|
|
1120
|
+
const b = in_shape[0];
|
|
1121
|
+
const d = in_shape[1];
|
|
1122
|
+
for (let i = 0; i < b; i++) {
|
|
1123
|
+
let sum = 0;
|
|
1124
|
+
for (let j = 0; j < d; j++) {
|
|
1125
|
+
sum += inBuf[i * d + j];
|
|
1126
|
+
}
|
|
1127
|
+
outBuf[i] = sum / d;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// js/ops/batchNorm2D.js
|
|
1132
|
+
function _cpuBatchNorm2D(node) {
|
|
1133
|
+
const input = node.inputs.input || node.inputs.x;
|
|
1134
|
+
const weight = node.inputs.weight || node.inputs.scale;
|
|
1135
|
+
const bias = node.inputs.bias || node.inputs.b;
|
|
1136
|
+
const running_mean = node.inputs.running_mean || node.inputs.mean;
|
|
1137
|
+
const running_var = node.inputs.running_var || node.inputs.var;
|
|
1138
|
+
const outBuf = node.outputs.out.buffer;
|
|
1139
|
+
const [b, h, w, c] = input.shape;
|
|
1140
|
+
const eps = node.params.eps || 1e-5;
|
|
1141
|
+
for (let batch = 0; batch < b; batch++) {
|
|
1142
|
+
for (let chan = 0; chan < c; chan++) {
|
|
1143
|
+
const w_val = weight.buffer[chan];
|
|
1144
|
+
const b_val = bias ? bias.buffer[chan] : 0;
|
|
1145
|
+
const rm_val = running_mean.buffer[chan];
|
|
1146
|
+
const rv_val = running_var.buffer[chan];
|
|
1147
|
+
for (let y = 0; y < h; y++) {
|
|
1148
|
+
for (let x = 0; x < w; x++) {
|
|
1149
|
+
const idx = ((batch * h + y) * w + x) * c + chan;
|
|
1150
|
+
const val = input.buffer[idx];
|
|
1151
|
+
outBuf[idx] = (val - rm_val) / Math.sqrt(rv_val + eps) * w_val + b_val;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// js/ops/div.js
|
|
1159
|
+
function _cpuDiv(node) {
|
|
1160
|
+
const a = node.inputs.a;
|
|
1161
|
+
const b = node.inputs.b;
|
|
1162
|
+
const out = node.outputs.out;
|
|
1163
|
+
const aBuf = a.buffer;
|
|
1164
|
+
const bBuf = b.buffer;
|
|
1165
|
+
const outBuf = out.buffer;
|
|
1166
|
+
if (bBuf.length === 1) {
|
|
1167
|
+
for (let i = 0; i < aBuf.length; i++) outBuf[i] = aBuf[i] / bBuf[0];
|
|
1168
|
+
} else {
|
|
1169
|
+
for (let i = 0; i < aBuf.length; i++) outBuf[i] = aBuf[i] / bBuf[i % bBuf.length];
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// js/ops/nonMaxSuppression.js
|
|
1174
|
+
function _cpuNonMaxSuppression(node) {
|
|
1175
|
+
const boxes = node.inputs.boxes;
|
|
1176
|
+
const scores = node.inputs.scores;
|
|
1177
|
+
const out = node.outputs.out;
|
|
1178
|
+
let max_output_boxes_per_class = 0;
|
|
1179
|
+
if (node.inputs.max_output_boxes_per_class) max_output_boxes_per_class = node.inputs.max_output_boxes_per_class.buffer[0];
|
|
1180
|
+
let iou_threshold = 0.5;
|
|
1181
|
+
if (node.inputs.iou_threshold) iou_threshold = node.inputs.iou_threshold.buffer[0];
|
|
1182
|
+
let score_threshold = 0;
|
|
1183
|
+
if (node.inputs.score_threshold) score_threshold = node.inputs.score_threshold.buffer[0];
|
|
1184
|
+
const num_batches = boxes.shape[0];
|
|
1185
|
+
const spatial_dimension = boxes.shape[1];
|
|
1186
|
+
const num_classes = scores.shape[1];
|
|
1187
|
+
let outIdx = 0;
|
|
1188
|
+
for (let b = 0; b < num_batches; b++) {
|
|
1189
|
+
for (let c = 0; c < num_classes; c++) {
|
|
1190
|
+
let candidates = [];
|
|
1191
|
+
for (let s = 0; s < spatial_dimension; s++) {
|
|
1192
|
+
const score = scores.buffer[b * (num_classes * spatial_dimension) + c * spatial_dimension + s];
|
|
1193
|
+
if (score >= score_threshold) {
|
|
1194
|
+
const y1 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 0];
|
|
1195
|
+
const x1 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 1];
|
|
1196
|
+
const y2 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 2];
|
|
1197
|
+
const x2 = boxes.buffer[b * (spatial_dimension * 4) + s * 4 + 3];
|
|
1198
|
+
candidates.push({ s, score, y1, x1, y2, x2 });
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
candidates.sort((a, b_) => b_.score - a.score);
|
|
1202
|
+
let selected = [];
|
|
1203
|
+
for (let i = 0; i < candidates.length && selected.length < max_output_boxes_per_class; i++) {
|
|
1204
|
+
const cand = candidates[i];
|
|
1205
|
+
let keep = true;
|
|
1206
|
+
for (let j = 0; j < selected.length; j++) {
|
|
1207
|
+
const sel = selected[j];
|
|
1208
|
+
const xx1 = Math.max(cand.x1, sel.x1);
|
|
1209
|
+
const yy1 = Math.max(cand.y1, sel.y1);
|
|
1210
|
+
const xx2 = Math.min(cand.x2, sel.x2);
|
|
1211
|
+
const yy2 = Math.min(cand.y2, sel.y2);
|
|
1212
|
+
const w = Math.max(0, xx2 - xx1);
|
|
1213
|
+
const h = Math.max(0, yy2 - yy1);
|
|
1214
|
+
const inter = w * h;
|
|
1215
|
+
const areaCand = (cand.x2 - cand.x1) * (cand.y2 - cand.y1);
|
|
1216
|
+
const areaSel = (sel.x2 - sel.x1) * (sel.y2 - sel.y1);
|
|
1217
|
+
const iou = inter / (areaCand + areaSel - inter);
|
|
1218
|
+
if (iou > iou_threshold) {
|
|
1219
|
+
keep = false;
|
|
1220
|
+
break;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (keep) {
|
|
1224
|
+
selected.push(cand);
|
|
1225
|
+
if (outIdx < out.buffer.length / 3) {
|
|
1226
|
+
out.buffer[outIdx * 3 + 0] = b;
|
|
1227
|
+
out.buffer[outIdx * 3 + 1] = c;
|
|
1228
|
+
out.buffer[outIdx * 3 + 2] = cand.s;
|
|
1229
|
+
outIdx++;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
while (outIdx < out.buffer.length / 3) {
|
|
1236
|
+
out.buffer[outIdx * 3 + 0] = -1;
|
|
1237
|
+
out.buffer[outIdx * 3 + 1] = -1;
|
|
1238
|
+
out.buffer[outIdx * 3 + 2] = -1;
|
|
1239
|
+
outIdx++;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// js/ops/gather.js
|
|
1244
|
+
function _cpuGather(node) {
|
|
1245
|
+
const input = node.inputs.input;
|
|
1246
|
+
const indices = node.inputs.indices;
|
|
1247
|
+
const out = node.outputs.out;
|
|
1248
|
+
let axis = node.params.axis || 0;
|
|
1249
|
+
if (axis < 0) axis += input.shape.length;
|
|
1250
|
+
const inShape = input.shape;
|
|
1251
|
+
const outShape = out.shape;
|
|
1252
|
+
const idxShape = indices.shape;
|
|
1253
|
+
let inStrides = new Array(inShape.length);
|
|
1254
|
+
let s = 1;
|
|
1255
|
+
for (let i = inShape.length - 1; i >= 0; i--) {
|
|
1256
|
+
inStrides[i] = s;
|
|
1257
|
+
s *= inShape[i];
|
|
1258
|
+
}
|
|
1259
|
+
let outStrides = new Array(outShape.length);
|
|
1260
|
+
s = 1;
|
|
1261
|
+
for (let i = outShape.length - 1; i >= 0; i--) {
|
|
1262
|
+
outStrides[i] = s;
|
|
1263
|
+
s *= outShape[i];
|
|
1264
|
+
}
|
|
1265
|
+
let idxStrides = new Array(idxShape.length);
|
|
1266
|
+
s = 1;
|
|
1267
|
+
for (let i = idxShape.length - 1; i >= 0; i--) {
|
|
1268
|
+
idxStrides[i] = s;
|
|
1269
|
+
s *= idxShape[i];
|
|
1270
|
+
}
|
|
1271
|
+
for (let i = 0; i < out.buffer.length; i++) {
|
|
1272
|
+
let temp = i;
|
|
1273
|
+
let outCoords = new Array(outShape.length);
|
|
1274
|
+
for (let d = 0; d < outShape.length; d++) {
|
|
1275
|
+
outCoords[d] = Math.floor(temp / outStrides[d]);
|
|
1276
|
+
temp %= outStrides[d];
|
|
1277
|
+
}
|
|
1278
|
+
let idxOffset = 0;
|
|
1279
|
+
for (let d = 0; d < idxShape.length; d++) {
|
|
1280
|
+
idxOffset += outCoords[axis + d] * idxStrides[d];
|
|
1281
|
+
}
|
|
1282
|
+
let gatherIdx = indices.buffer[idxOffset];
|
|
1283
|
+
if (gatherIdx < 0) {
|
|
1284
|
+
out.buffer[i] = -1;
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
let inOffset = 0;
|
|
1288
|
+
for (let d = 0; d < axis; d++) {
|
|
1289
|
+
inOffset += outCoords[d] * inStrides[d];
|
|
1290
|
+
}
|
|
1291
|
+
inOffset += gatherIdx * inStrides[axis];
|
|
1292
|
+
for (let d = axis + 1; d < inShape.length; d++) {
|
|
1293
|
+
inOffset += outCoords[d - 1 + idxShape.length] * inStrides[d];
|
|
1294
|
+
}
|
|
1295
|
+
out.buffer[i] = input.buffer[inOffset];
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// js/ops/gatherElements.js
|
|
1300
|
+
function _cpuGatherElements(node) {
|
|
1301
|
+
const data = node.inputs.input || node.inputs.data;
|
|
1302
|
+
const indices = node.inputs.indices;
|
|
1303
|
+
const out = node.outputs.out;
|
|
1304
|
+
const dBuf = data.buffer;
|
|
1305
|
+
const iBuf = indices.buffer;
|
|
1306
|
+
const oBuf = out.buffer;
|
|
1307
|
+
const dShape = data.shape;
|
|
1308
|
+
const iShape = indices.shape;
|
|
1309
|
+
let axis = node.params.axis !== void 0 ? node.params.axis : 0;
|
|
1310
|
+
if (axis < 0) axis += dShape.length;
|
|
1311
|
+
const dStrides = new Array(dShape.length);
|
|
1312
|
+
{
|
|
1313
|
+
let s = 1;
|
|
1314
|
+
for (let k = dShape.length - 1; k >= 0; k--) {
|
|
1315
|
+
dStrides[k] = s;
|
|
1316
|
+
s *= dShape[k];
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
const iStrides = new Array(iShape.length);
|
|
1320
|
+
{
|
|
1321
|
+
let s = 1;
|
|
1322
|
+
for (let k = iShape.length - 1; k >= 0; k--) {
|
|
1323
|
+
iStrides[k] = s;
|
|
1324
|
+
s *= iShape[k];
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const rank = iShape.length;
|
|
1328
|
+
const coord = new Array(rank);
|
|
1329
|
+
for (let lin = 0; lin < oBuf.length; lin++) {
|
|
1330
|
+
let rem = lin;
|
|
1331
|
+
for (let k = 0; k < rank; k++) {
|
|
1332
|
+
coord[k] = Math.floor(rem / iStrides[k]);
|
|
1333
|
+
rem %= iStrides[k];
|
|
1334
|
+
}
|
|
1335
|
+
let idx = iBuf[lin] | 0;
|
|
1336
|
+
if (idx < 0) idx += dShape[axis];
|
|
1337
|
+
let off = 0;
|
|
1338
|
+
for (let k = 0; k < rank; k++) off += (k === axis ? idx : coord[k]) * dStrides[k];
|
|
1339
|
+
oBuf[lin] = dBuf[off];
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// js/ops/crossAttention.js
|
|
1344
|
+
function _cpuCrossAttention(node) {
|
|
1345
|
+
const q_in = node.inputs.q.buffer;
|
|
1346
|
+
const kv_in = node.inputs.kv.buffer;
|
|
1347
|
+
const wBuf = node.inputs.weight.buffer;
|
|
1348
|
+
const scale = node.inputs.scale ? node.inputs.scale.buffer : null;
|
|
1349
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
1350
|
+
const outBuf = node.outputs.out.buffer;
|
|
1351
|
+
const seq_len_q = node.inputs.q.shape[1];
|
|
1352
|
+
const seq_len_kv = node.inputs.kv.shape[1];
|
|
1353
|
+
const d_model = node.outputs.out.shape[2];
|
|
1354
|
+
const num_heads = node.params.heads || 8;
|
|
1355
|
+
const head_dim = d_model / num_heads;
|
|
1356
|
+
const scale_factor = 1 / Math.sqrt(head_dim);
|
|
1357
|
+
for (let h = 0; h < num_heads; h++) {
|
|
1358
|
+
for (let q = 0; q < seq_len_q; q++) {
|
|
1359
|
+
const q_proj = new Float32Array(head_dim);
|
|
1360
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1361
|
+
let sum = 0;
|
|
1362
|
+
const out_col = h * head_dim + d;
|
|
1363
|
+
for (let i = 0; i < d_model; i++) {
|
|
1364
|
+
sum += q_in[q * d_model + i] * wBuf[out_col * d_model + i];
|
|
1365
|
+
}
|
|
1366
|
+
if (scale) sum *= scale[out_col];
|
|
1367
|
+
if (bias) sum += bias[out_col];
|
|
1368
|
+
q_proj[d] = sum;
|
|
1369
|
+
}
|
|
1370
|
+
const logits = new Float32Array(seq_len_kv);
|
|
1371
|
+
let max_logit = -Infinity;
|
|
1372
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
1373
|
+
let score = 0;
|
|
1374
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1375
|
+
let k_val = 0;
|
|
1376
|
+
const out_col = d_model + h * head_dim + d;
|
|
1377
|
+
for (let i = 0; i < d_model; i++) {
|
|
1378
|
+
k_val += kv_in[k * d_model + i] * wBuf[out_col * d_model + i];
|
|
1379
|
+
}
|
|
1380
|
+
if (scale) k_val *= scale[out_col];
|
|
1381
|
+
if (bias) k_val += bias[out_col];
|
|
1382
|
+
score += q_proj[d] * k_val;
|
|
1383
|
+
}
|
|
1384
|
+
score *= scale_factor;
|
|
1385
|
+
logits[k] = score;
|
|
1386
|
+
if (score > max_logit) max_logit = score;
|
|
1387
|
+
}
|
|
1388
|
+
let sum_exp = 0;
|
|
1389
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
1390
|
+
const exp_val = Math.exp(logits[k] - max_logit);
|
|
1391
|
+
logits[k] = exp_val;
|
|
1392
|
+
sum_exp += exp_val;
|
|
1393
|
+
}
|
|
1394
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1395
|
+
let out_val = 0;
|
|
1396
|
+
for (let k = 0; k < seq_len_kv; k++) {
|
|
1397
|
+
const w = logits[k] / sum_exp;
|
|
1398
|
+
let v_val = 0;
|
|
1399
|
+
const out_col = d_model * 2 + h * head_dim + d;
|
|
1400
|
+
for (let i = 0; i < d_model; i++) {
|
|
1401
|
+
v_val += kv_in[k * d_model + i] * wBuf[out_col * d_model + i];
|
|
1402
|
+
}
|
|
1403
|
+
if (scale) v_val *= scale[out_col];
|
|
1404
|
+
if (bias) v_val += bias[out_col];
|
|
1405
|
+
out_val += w * v_val;
|
|
1406
|
+
}
|
|
1407
|
+
outBuf[q * d_model + h * head_dim + d] = out_val;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// js/ops/spatialSoftargmaxY.js
|
|
1414
|
+
function _cpuSpatialSoftargmaxY(node) {
|
|
1415
|
+
const input = node.inputs.input;
|
|
1416
|
+
const output = node.outputs.out;
|
|
1417
|
+
const inBuf = input.buffer;
|
|
1418
|
+
const outBuf = output.buffer;
|
|
1419
|
+
const h = input.shape[1];
|
|
1420
|
+
const w = input.shape[2];
|
|
1421
|
+
const c = input.shape[3];
|
|
1422
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1423
|
+
const outBase = ch * w;
|
|
1424
|
+
for (let x = 0; x < w; x++) {
|
|
1425
|
+
let maxLogit = -Infinity;
|
|
1426
|
+
for (let y = 0; y < h; y++) {
|
|
1427
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
1428
|
+
if (v > maxLogit) maxLogit = v;
|
|
1429
|
+
}
|
|
1430
|
+
let denom = 0;
|
|
1431
|
+
let weighted = 0;
|
|
1432
|
+
for (let y = 0; y < h; y++) {
|
|
1433
|
+
const ev = Math.exp(inBuf[(y * w + x) * c + ch] - maxLogit);
|
|
1434
|
+
denom += ev;
|
|
1435
|
+
weighted += ev * ((y + 0.5) / h);
|
|
1436
|
+
}
|
|
1437
|
+
outBuf[outBase + x] = denom > 0 ? weighted / denom : 0;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// js/ops/transpose.js
|
|
1443
|
+
function _cpuTranspose(node) {
|
|
1444
|
+
const input = node.inputs.input;
|
|
1445
|
+
const inBuf = input.buffer;
|
|
1446
|
+
const inShape = input.shape;
|
|
1447
|
+
const outBuf = node.outputs.out.buffer;
|
|
1448
|
+
const perm = node.params.perm || [...Array(inShape.length).keys()].reverse();
|
|
1449
|
+
const inStrides = new Array(inShape.length);
|
|
1450
|
+
let s = 1;
|
|
1451
|
+
for (let i = inShape.length - 1; i >= 0; i--) {
|
|
1452
|
+
inStrides[i] = s;
|
|
1453
|
+
s *= inShape[i];
|
|
1454
|
+
}
|
|
1455
|
+
const outShape = perm.map((p) => inShape[p]);
|
|
1456
|
+
const outStrides = new Array(outShape.length);
|
|
1457
|
+
s = 1;
|
|
1458
|
+
for (let i = outShape.length - 1; i >= 0; i--) {
|
|
1459
|
+
outStrides[i] = s;
|
|
1460
|
+
s *= outShape[i];
|
|
1461
|
+
}
|
|
1462
|
+
const elements = inBuf.length;
|
|
1463
|
+
for (let i = 0; i < elements; i++) {
|
|
1464
|
+
let inIdx = 0;
|
|
1465
|
+
let temp = i;
|
|
1466
|
+
for (let j = 0; j < outShape.length; j++) {
|
|
1467
|
+
const outCoord = Math.floor(temp / outStrides[j]);
|
|
1468
|
+
temp %= outStrides[j];
|
|
1469
|
+
inIdx += outCoord * inStrides[perm[j]];
|
|
1470
|
+
}
|
|
1471
|
+
outBuf[i] = inBuf[inIdx];
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
// js/ops/crossSDPA.js
|
|
1476
|
+
function _cpuCrossSDPA(node) {
|
|
1477
|
+
const q = node.inputs.q.buffer;
|
|
1478
|
+
const k = node.inputs.k.buffer;
|
|
1479
|
+
const v = node.inputs.v.buffer;
|
|
1480
|
+
const outBuf = node.outputs.out.buffer;
|
|
1481
|
+
const seqQ = node.inputs.q.shape[1];
|
|
1482
|
+
const seqKV = node.inputs.k.shape[1];
|
|
1483
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
1484
|
+
const heads = node.params.heads || 8;
|
|
1485
|
+
const head_dim = d_model / heads;
|
|
1486
|
+
const scale = 1 / Math.sqrt(head_dim);
|
|
1487
|
+
for (let h = 0; h < heads; h++) {
|
|
1488
|
+
for (let qi = 0; qi < seqQ; qi++) {
|
|
1489
|
+
const logits = new Float32Array(seqKV);
|
|
1490
|
+
let mx = -Infinity;
|
|
1491
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
1492
|
+
let s = 0;
|
|
1493
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1494
|
+
s += q[qi * d_model + h * head_dim + d] * k[ki * d_model + h * head_dim + d];
|
|
1495
|
+
}
|
|
1496
|
+
s *= scale;
|
|
1497
|
+
logits[ki] = s;
|
|
1498
|
+
if (s > mx) mx = s;
|
|
1499
|
+
}
|
|
1500
|
+
let sum = 0;
|
|
1501
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
1502
|
+
const e = Math.exp(logits[ki] - mx);
|
|
1503
|
+
logits[ki] = e;
|
|
1504
|
+
sum += e;
|
|
1505
|
+
}
|
|
1506
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1507
|
+
let o = 0;
|
|
1508
|
+
for (let ki = 0; ki < seqKV; ki++) {
|
|
1509
|
+
o += logits[ki] / sum * v[ki * d_model + h * head_dim + d];
|
|
1510
|
+
}
|
|
1511
|
+
outBuf[qi * d_model + h * head_dim + d] = o;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
// js/ops/meanHeight.js
|
|
1518
|
+
function _cpuMeanHeight(node) {
|
|
1519
|
+
const input = node.inputs.input;
|
|
1520
|
+
const output = node.outputs.out;
|
|
1521
|
+
const [, h, w, c] = input.shape;
|
|
1522
|
+
const inBuf = input.buffer;
|
|
1523
|
+
const outBuf = output.buffer;
|
|
1524
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1525
|
+
for (let x = 0; x < w; x++) {
|
|
1526
|
+
let sum = 0;
|
|
1527
|
+
for (let y = 0; y < h; y++) {
|
|
1528
|
+
sum += inBuf[(y * w + x) * c + ch];
|
|
1529
|
+
}
|
|
1530
|
+
outBuf[ch * w + x] = sum / h;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
// js/ops/maxPool2D.js
|
|
1536
|
+
function _cpuMaxPool2D(node) {
|
|
1537
|
+
const input = node.inputs.input;
|
|
1538
|
+
const output = node.outputs.out;
|
|
1539
|
+
const [n, h, w, c] = input.shape;
|
|
1540
|
+
const ky = node.params.kernel[0];
|
|
1541
|
+
const kx = node.params.kernel[1];
|
|
1542
|
+
const sy = node.params.stride[0];
|
|
1543
|
+
const sx = node.params.stride[1];
|
|
1544
|
+
const pad_y = node.params.padding ? node.params.padding[0] : 0;
|
|
1545
|
+
const pad_x = node.params.padding ? node.params.padding[1] : 0;
|
|
1546
|
+
const out_h = output.shape[1];
|
|
1547
|
+
const out_w = output.shape[2];
|
|
1548
|
+
const inBuf = input.buffer;
|
|
1549
|
+
const outBuf = output.buffer;
|
|
1550
|
+
for (let b = 0; b < n; b++) {
|
|
1551
|
+
for (let oy = 0; oy < out_h; oy++) {
|
|
1552
|
+
for (let ox = 0; ox < out_w; ox++) {
|
|
1553
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1554
|
+
let best = -Infinity;
|
|
1555
|
+
for (let dy = 0; dy < ky; dy++) {
|
|
1556
|
+
for (let dx = 0; dx < kx; dx++) {
|
|
1557
|
+
const ih = oy * sy + dy - pad_y;
|
|
1558
|
+
const iw = ox * sx + dx - pad_x;
|
|
1559
|
+
if (ih >= 0 && ih < h && iw >= 0 && iw < w) {
|
|
1560
|
+
const v = inBuf[((b * h + ih) * w + iw) * c + ch];
|
|
1561
|
+
if (v > best) best = v;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
outBuf[((b * out_h + oy) * out_w + ox) * c + ch] = best;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
// js/ops/interp1D.js
|
|
1573
|
+
function _cpuInterp1D(node) {
|
|
1574
|
+
const input = node.inputs.input;
|
|
1575
|
+
const output = node.outputs.out;
|
|
1576
|
+
const [c, in_l] = input.shape.slice(1);
|
|
1577
|
+
const out_l = node.params.size;
|
|
1578
|
+
const inBuf = input.buffer;
|
|
1579
|
+
const outBuf = output.buffer;
|
|
1580
|
+
const scale = in_l / out_l;
|
|
1581
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1582
|
+
for (let x = 0; x < out_l; x++) {
|
|
1583
|
+
let pos = (x + 0.5) * scale - 0.5;
|
|
1584
|
+
if (pos < 0) pos = 0;
|
|
1585
|
+
if (pos > in_l - 1) pos = in_l - 1;
|
|
1586
|
+
const x0 = Math.floor(pos);
|
|
1587
|
+
const x1 = x0 + 1 < in_l ? x0 + 1 : x0;
|
|
1588
|
+
const dx = pos - x0;
|
|
1589
|
+
const v0 = inBuf[ch * in_l + x0];
|
|
1590
|
+
const v1 = inBuf[ch * in_l + x1];
|
|
1591
|
+
outBuf[ch * out_l + x] = v0 + dx * (v1 - v0);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// js/ops/profileX.js
|
|
1597
|
+
function _cpuProfileX(node) {
|
|
1598
|
+
const input = node.inputs.input;
|
|
1599
|
+
const output = node.outputs.out;
|
|
1600
|
+
const [, h, w, c] = input.shape;
|
|
1601
|
+
const inBuf = input.buffer;
|
|
1602
|
+
const outBuf = output.buffer;
|
|
1603
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1604
|
+
for (let x = 0; x < w; x++) {
|
|
1605
|
+
let max_v = -Infinity;
|
|
1606
|
+
let sum_v = 0;
|
|
1607
|
+
for (let y = 0; y < h; y++) {
|
|
1608
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
1609
|
+
if (v > max_v) max_v = v;
|
|
1610
|
+
sum_v += v;
|
|
1611
|
+
}
|
|
1612
|
+
outBuf[ch * w + x] = max_v;
|
|
1613
|
+
outBuf[(c + ch) * w + x] = sum_v / h;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// js/ops/profileY.js
|
|
1619
|
+
function _cpuProfileY(node) {
|
|
1620
|
+
const input = node.inputs.input;
|
|
1621
|
+
const output = node.outputs.out;
|
|
1622
|
+
const [, h, w, c] = input.shape;
|
|
1623
|
+
const inBuf = input.buffer;
|
|
1624
|
+
const outBuf = output.buffer;
|
|
1625
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1626
|
+
for (let y = 0; y < h; y++) {
|
|
1627
|
+
let max_v = -Infinity;
|
|
1628
|
+
let sum_v = 0;
|
|
1629
|
+
for (let x = 0; x < w; x++) {
|
|
1630
|
+
const v = inBuf[(y * w + x) * c + ch];
|
|
1631
|
+
if (v > max_v) max_v = v;
|
|
1632
|
+
sum_v += v;
|
|
1633
|
+
}
|
|
1634
|
+
outBuf[ch * h + y] = max_v;
|
|
1635
|
+
outBuf[(c + ch) * h + y] = sum_v / w;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// js/ops/concat2.js
|
|
1641
|
+
function _cpuConcat2(node) {
|
|
1642
|
+
const outBuf = node.outputs.out.buffer;
|
|
1643
|
+
let off = 0;
|
|
1644
|
+
const entries = Object.entries(node.inputs).sort(([a], [b]) => {
|
|
1645
|
+
const ai = /^input(\d+)$/.exec(a);
|
|
1646
|
+
const bi = /^input(\d+)$/.exec(b);
|
|
1647
|
+
if (ai && bi) return Number(ai[1]) - Number(bi[1]);
|
|
1648
|
+
return a.localeCompare(b);
|
|
1649
|
+
});
|
|
1650
|
+
for (const [, t] of entries) {
|
|
1651
|
+
outBuf.set(t.buffer, off);
|
|
1652
|
+
off += t.buffer.length;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
// js/ops/upsample2x.js
|
|
1657
|
+
function _cpuUpsample2x(node) {
|
|
1658
|
+
const input = node.inputs.input;
|
|
1659
|
+
const output = node.outputs.out;
|
|
1660
|
+
const [n, h, w, c] = input.shape;
|
|
1661
|
+
const inBuf = input.buffer;
|
|
1662
|
+
const outBuf = output.buffer;
|
|
1663
|
+
for (let b = 0; b < n; b++) {
|
|
1664
|
+
for (let y = 0; y < h; y++) {
|
|
1665
|
+
for (let x = 0; x < w; x++) {
|
|
1666
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1667
|
+
const v = inBuf[((b * h + y) * w + x) * c + ch];
|
|
1668
|
+
const oy = y * 2;
|
|
1669
|
+
const ox = x * 2;
|
|
1670
|
+
outBuf[((b * h * 2 + oy) * w * 2 + ox) * c + ch] = v;
|
|
1671
|
+
outBuf[((b * h * 2 + oy) * w * 2 + ox + 1) * c + ch] = v;
|
|
1672
|
+
outBuf[((b * h * 2 + oy + 1) * w * 2 + ox) * c + ch] = v;
|
|
1673
|
+
outBuf[((b * h * 2 + oy + 1) * w * 2 + ox + 1) * c + ch] = v;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// js/ops/sDPA.js
|
|
1681
|
+
function _cpuSDPA(node) {
|
|
1682
|
+
const qkv = node.inputs.qkv.buffer;
|
|
1683
|
+
const outBuf = node.outputs.out.buffer;
|
|
1684
|
+
const seq_len = node.inputs.qkv.shape[1];
|
|
1685
|
+
const d_model = node.outputs.out.shape[2];
|
|
1686
|
+
const num_heads = node.params.heads || 8;
|
|
1687
|
+
const head_dim = d_model / num_heads;
|
|
1688
|
+
const scale = node.params.scale !== void 0 ? node.params.scale : 1 / Math.sqrt(head_dim);
|
|
1689
|
+
for (let h = 0; h < num_heads; h++) {
|
|
1690
|
+
for (let q = 0; q < seq_len; q++) {
|
|
1691
|
+
const logits = new Float32Array(seq_len);
|
|
1692
|
+
let max_logit = -Infinity;
|
|
1693
|
+
for (let k = 0; k <= q; k++) {
|
|
1694
|
+
let score = 0;
|
|
1695
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1696
|
+
const q_val = qkv[q * (d_model * 3) + h * head_dim + d];
|
|
1697
|
+
const k_val = qkv[k * (d_model * 3) + d_model + h * head_dim + d];
|
|
1698
|
+
score += q_val * k_val;
|
|
1699
|
+
}
|
|
1700
|
+
score *= scale;
|
|
1701
|
+
logits[k] = score;
|
|
1702
|
+
if (score > max_logit) max_logit = score;
|
|
1703
|
+
}
|
|
1704
|
+
let sum_exp = 0;
|
|
1705
|
+
for (let k = 0; k <= q; k++) {
|
|
1706
|
+
const exp_val = Math.exp(logits[k] - max_logit);
|
|
1707
|
+
logits[k] = exp_val;
|
|
1708
|
+
sum_exp += exp_val;
|
|
1709
|
+
}
|
|
1710
|
+
for (let d = 0; d < head_dim; d++) {
|
|
1711
|
+
let out_val = 0;
|
|
1712
|
+
for (let k = 0; k <= q; k++) {
|
|
1713
|
+
const w = logits[k] / sum_exp;
|
|
1714
|
+
const v_val = qkv[k * (d_model * 3) + d_model * 2 + h * head_dim + d];
|
|
1715
|
+
out_val += w * v_val;
|
|
1716
|
+
}
|
|
1717
|
+
outBuf[q * d_model + h * head_dim + d] = out_val;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// js/ops/embedding.js
|
|
1724
|
+
function _cpuEmbedding(node) {
|
|
1725
|
+
const tokens = node.inputs.input.buffer;
|
|
1726
|
+
const wBuf = node.inputs.weight.buffer;
|
|
1727
|
+
const outBuf = node.outputs.out.buffer;
|
|
1728
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
1729
|
+
const seq_len = tokens.length;
|
|
1730
|
+
for (let i = 0; i < seq_len; i++) {
|
|
1731
|
+
const token_id = tokens[i];
|
|
1732
|
+
for (let j = 0; j < d_model; j++) {
|
|
1733
|
+
outBuf[i * d_model + j] = wBuf[token_id * d_model + j];
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
// js/ops/mul.js
|
|
1739
|
+
function _cpuMul(node) {
|
|
1740
|
+
const a = node.inputs.a;
|
|
1741
|
+
const b = node.inputs.b;
|
|
1742
|
+
const outBuf = node.outputs.out.buffer;
|
|
1743
|
+
let aBuf = a.buffer;
|
|
1744
|
+
let bBuf = b.buffer;
|
|
1745
|
+
let aShape = a.shape;
|
|
1746
|
+
let bShape = b.shape;
|
|
1747
|
+
if (aBuf.length < bBuf.length) {
|
|
1748
|
+
let temp = aBuf;
|
|
1749
|
+
aBuf = bBuf;
|
|
1750
|
+
bBuf = temp;
|
|
1751
|
+
let tempS = aShape;
|
|
1752
|
+
aShape = bShape;
|
|
1753
|
+
bShape = tempS;
|
|
1754
|
+
}
|
|
1755
|
+
const elements = aBuf.length;
|
|
1756
|
+
if (bBuf.length === 1) {
|
|
1757
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[0];
|
|
1758
|
+
} else if (bBuf.length === elements) {
|
|
1759
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i];
|
|
1760
|
+
} else if (aShape.length === 4 && bBuf.length === aShape[3]) {
|
|
1761
|
+
const c = aShape[3];
|
|
1762
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i % c];
|
|
1763
|
+
} else if (aShape.length === 4 && bShape.length === 4 && bShape[0] === 1 && bShape[1] === 1 && bShape[2] === 1 && bShape[3] === aShape[3]) {
|
|
1764
|
+
const c = aShape[3];
|
|
1765
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] * bBuf[i % c];
|
|
1766
|
+
} else {
|
|
1767
|
+
console.warn("[VolvoxAI CPU] Executing Mul is not fully implemented yet for shapes", a.shape, b.shape);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// js/ops/add.js
|
|
1772
|
+
function _cpuAdd(node) {
|
|
1773
|
+
const a = node.inputs.a;
|
|
1774
|
+
const b = node.inputs.b;
|
|
1775
|
+
const outBuf = node.outputs.out.buffer;
|
|
1776
|
+
let aBuf = a.buffer;
|
|
1777
|
+
let bBuf = b.buffer;
|
|
1778
|
+
let aShape = a.shape;
|
|
1779
|
+
let bShape = b.shape;
|
|
1780
|
+
if (aBuf.length < bBuf.length) {
|
|
1781
|
+
let temp = aBuf;
|
|
1782
|
+
aBuf = bBuf;
|
|
1783
|
+
bBuf = temp;
|
|
1784
|
+
let tempS = aShape;
|
|
1785
|
+
aShape = bShape;
|
|
1786
|
+
bShape = tempS;
|
|
1787
|
+
}
|
|
1788
|
+
const elements = aBuf.length;
|
|
1789
|
+
if (bBuf.length === 1) {
|
|
1790
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] + bBuf[0];
|
|
1791
|
+
} else if (bBuf.length === elements) {
|
|
1792
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] + bBuf[i];
|
|
1793
|
+
} else if (aShape.length === 4 && bBuf.length === aShape[3]) {
|
|
1794
|
+
const c = aShape[3];
|
|
1795
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] + bBuf[i % c];
|
|
1796
|
+
} else if (aShape.length === 4 && bShape.length === 4 && bShape[0] === 1 && bShape[1] === 1 && bShape[2] === 1 && bShape[3] === aShape[3]) {
|
|
1797
|
+
const c = aShape[3];
|
|
1798
|
+
for (let i = 0; i < elements; i++) outBuf[i] = aBuf[i] + bBuf[i % c];
|
|
1799
|
+
} else {
|
|
1800
|
+
console.warn("[VolvoxAI CPU] Executing Add is not fully implemented yet for shapes", a.shape, b.shape);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// js/ops/globalAveragePool.js
|
|
1805
|
+
function _cpuGlobalAveragePool(node) {
|
|
1806
|
+
const input = node.inputs.input;
|
|
1807
|
+
const output = node.outputs.out;
|
|
1808
|
+
const B = input.shape[0];
|
|
1809
|
+
const H = input.shape[1];
|
|
1810
|
+
const W = input.shape[2];
|
|
1811
|
+
const C = input.shape[3];
|
|
1812
|
+
const spatial = H * W;
|
|
1813
|
+
for (let b = 0; b < B; b++) {
|
|
1814
|
+
for (let c = 0; c < C; c++) {
|
|
1815
|
+
let sum = 0;
|
|
1816
|
+
for (let i = 0; i < spatial; i++) {
|
|
1817
|
+
const y = Math.floor(i / W);
|
|
1818
|
+
const x = i - y * W;
|
|
1819
|
+
sum += input.buffer[((b * H + y) * W + x) * C + c];
|
|
1820
|
+
}
|
|
1821
|
+
output.buffer[b * C + c] = sum / spatial;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// js/ops/clip.js
|
|
1827
|
+
function _cpuClip(node) {
|
|
1828
|
+
const input = node.inputs.input;
|
|
1829
|
+
const output = node.outputs.out;
|
|
1830
|
+
let min = node.params.min !== void 0 ? node.params.min : -Infinity;
|
|
1831
|
+
let max = node.params.max !== void 0 ? node.params.max : Infinity;
|
|
1832
|
+
if (node.inputs.min) min = node.inputs.min.buffer[0];
|
|
1833
|
+
if (node.inputs.max) max = node.inputs.max.buffer[0];
|
|
1834
|
+
for (let i = 0; i < input.buffer.length; i++) {
|
|
1835
|
+
let v = input.buffer[i];
|
|
1836
|
+
if (v < min) v = min;
|
|
1837
|
+
if (v > max) v = max;
|
|
1838
|
+
output.buffer[i] = v;
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// js/ops/reshape.js
|
|
1843
|
+
function _cpuReshape(node) {
|
|
1844
|
+
const input = node.inputs.input;
|
|
1845
|
+
const output = node.outputs.out;
|
|
1846
|
+
output.buffer.set(input.buffer);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// js/ops/split.js
|
|
1850
|
+
function _cpuSplit(node) {
|
|
1851
|
+
const input = node.inputs.input;
|
|
1852
|
+
const inBuf = input.buffer;
|
|
1853
|
+
const inShape = input.shape;
|
|
1854
|
+
let axis = node.params.axis || 0;
|
|
1855
|
+
if (axis < 0) axis += inShape.length;
|
|
1856
|
+
const outKeys = Object.keys(node.outputs).sort();
|
|
1857
|
+
const numOutputs = outKeys.length;
|
|
1858
|
+
const splitSize = inShape[axis] / numOutputs;
|
|
1859
|
+
let outerSize = 1;
|
|
1860
|
+
for (let i = 0; i < axis; i++) outerSize *= inShape[i];
|
|
1861
|
+
let innerSize = 1;
|
|
1862
|
+
for (let i = axis + 1; i < inShape.length; i++) innerSize *= inShape[i];
|
|
1863
|
+
const chunkSize = splitSize * innerSize;
|
|
1864
|
+
for (let o = 0; o < numOutputs; o++) {
|
|
1865
|
+
const outBuf = node.outputs[outKeys[o]].buffer;
|
|
1866
|
+
for (let i = 0; i < outerSize; i++) {
|
|
1867
|
+
const inOffset = (i * inShape[axis] + o * splitSize) * innerSize;
|
|
1868
|
+
const outOffset = i * chunkSize;
|
|
1869
|
+
outBuf.set(inBuf.subarray(inOffset, inOffset + chunkSize), outOffset);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// js/ops/resize.js
|
|
1875
|
+
function _cpuResize(node) {
|
|
1876
|
+
const inp = node.inputs.input, out = node.outputs.out;
|
|
1877
|
+
const [b, inH, inW, c] = inp.shape;
|
|
1878
|
+
const [, outH, outW] = out.shape;
|
|
1879
|
+
const src = inp.buffer, dst = out.buffer;
|
|
1880
|
+
if (node.opType === "ResizeNearest2D" || node.params.mode === "nearest") {
|
|
1881
|
+
for (let n = 0; n < b; n++) {
|
|
1882
|
+
for (let y = 0; y < outH; y++) {
|
|
1883
|
+
let iy = Math.floor(y * inH / outH);
|
|
1884
|
+
if (iy >= inH) iy = inH - 1;
|
|
1885
|
+
for (let x = 0; x < outW; x++) {
|
|
1886
|
+
let ix = Math.floor(x * inW / outW);
|
|
1887
|
+
if (ix >= inW) ix = inW - 1;
|
|
1888
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1889
|
+
dst[((n * outH + y) * outW + x) * c + ch] = src[((n * inH + iy) * inW + ix) * c + ch];
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
const sy = inH / outH, sx = inW / outW;
|
|
1897
|
+
for (let n = 0; n < b; n++) {
|
|
1898
|
+
for (let y = 0; y < outH; y++) {
|
|
1899
|
+
let iy = (y + 0.5) * sy - 0.5;
|
|
1900
|
+
if (iy < 0) iy = 0;
|
|
1901
|
+
const y0 = Math.min(inH - 1, Math.floor(iy)), y1 = Math.min(inH - 1, y0 + 1), dy = iy - y0;
|
|
1902
|
+
for (let x = 0; x < outW; x++) {
|
|
1903
|
+
let ix = (x + 0.5) * sx - 0.5;
|
|
1904
|
+
if (ix < 0) ix = 0;
|
|
1905
|
+
const x0 = Math.min(inW - 1, Math.floor(ix)), x1 = Math.min(inW - 1, x0 + 1), dx = ix - x0;
|
|
1906
|
+
for (let ch = 0; ch < c; ch++) {
|
|
1907
|
+
const v00 = src[((n * inH + y0) * inW + x0) * c + ch];
|
|
1908
|
+
const v01 = src[((n * inH + y0) * inW + x1) * c + ch];
|
|
1909
|
+
const v10 = src[((n * inH + y1) * inW + x0) * c + ch];
|
|
1910
|
+
const v11 = src[((n * inH + y1) * inW + x1) * c + ch];
|
|
1911
|
+
dst[((n * outH + y) * outW + x) * c + ch] = v00 * (1 - dy) * (1 - dx) + v01 * (1 - dy) * dx + v10 * dy * (1 - dx) + v11 * dy * dx;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// js/ops/sigmoid.js
|
|
1919
|
+
function _cpuSigmoid(node) {
|
|
1920
|
+
const inBuf = node.inputs.input.buffer;
|
|
1921
|
+
const outBuf = node.outputs.out.buffer;
|
|
1922
|
+
for (let i = 0; i < inBuf.length; i++) outBuf[i] = 1 / (1 + Math.exp(-inBuf[i]));
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
// js/ops/hardSigmoid.js
|
|
1926
|
+
function _cpuHardSigmoid(node) {
|
|
1927
|
+
const inBuf = node.inputs.input.buffer;
|
|
1928
|
+
const outBuf = node.outputs.out.buffer;
|
|
1929
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
1930
|
+
const x = inBuf[i];
|
|
1931
|
+
let v = x + 3;
|
|
1932
|
+
if (v < 0) v = 0;
|
|
1933
|
+
else if (v > 6) v = 6;
|
|
1934
|
+
outBuf[i] = v / 6;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
// js/ops/hardSwish.js
|
|
1939
|
+
function _cpuHardSwish(node) {
|
|
1940
|
+
const inBuf = node.inputs.input.buffer;
|
|
1941
|
+
const outBuf = node.outputs.out.buffer;
|
|
1942
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
1943
|
+
const x = inBuf[i];
|
|
1944
|
+
let v = x + 3;
|
|
1945
|
+
if (v < 0) v = 0;
|
|
1946
|
+
else if (v > 6) v = 6;
|
|
1947
|
+
outBuf[i] = x * v / 6;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// js/ops/reLU.js
|
|
1952
|
+
function _cpuReLU(node) {
|
|
1953
|
+
const inBuf = node.inputs.input.buffer;
|
|
1954
|
+
const outBuf = node.outputs.out.buffer;
|
|
1955
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
1956
|
+
outBuf[i] = inBuf[i] > 0 ? inBuf[i] : 0;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// js/ops/leakyReLU.js
|
|
1961
|
+
function _cpuLeakyReLU(node) {
|
|
1962
|
+
const inBuf = node.inputs.input.buffer;
|
|
1963
|
+
const outBuf = node.outputs.out.buffer;
|
|
1964
|
+
const alpha = node.params.alpha !== void 0 ? node.params.alpha : 0.01;
|
|
1965
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
1966
|
+
const v = inBuf[i];
|
|
1967
|
+
outBuf[i] = v > 0 ? v : alpha * v;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// js/ops/gELU.js
|
|
1972
|
+
function _cpuGELU(node) {
|
|
1973
|
+
const inBuf = node.inputs.input.buffer;
|
|
1974
|
+
const outBuf = node.outputs.out.buffer;
|
|
1975
|
+
for (let i = 0; i < inBuf.length; i++) {
|
|
1976
|
+
const x = inBuf[i];
|
|
1977
|
+
outBuf[i] = 0.5 * x * (1 + Math.tanh(0.7978845608 * (x + 0.044715 * x * x * x)));
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// js/ops/layerNorm.js
|
|
1982
|
+
function _cpuLayerNorm(node) {
|
|
1983
|
+
const inBuf = node.inputs.input.buffer;
|
|
1984
|
+
const wBuf = node.inputs.weight.buffer;
|
|
1985
|
+
const bBuf = node.inputs.bias.buffer;
|
|
1986
|
+
const outBuf = node.outputs.out.buffer;
|
|
1987
|
+
const d_model = node.params.d_model;
|
|
1988
|
+
const seq_len = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
1989
|
+
for (let i = 0; i < seq_len; i++) {
|
|
1990
|
+
const offset = i * d_model;
|
|
1991
|
+
let sum = 0, sq_sum = 0;
|
|
1992
|
+
for (let j = 0; j < d_model; j++) {
|
|
1993
|
+
const val = inBuf[offset + j];
|
|
1994
|
+
sum += val;
|
|
1995
|
+
sq_sum += val * val;
|
|
1996
|
+
}
|
|
1997
|
+
const mean = sum / d_model;
|
|
1998
|
+
const variance = sq_sum / d_model - mean * mean;
|
|
1999
|
+
const inv_std = 1 / Math.sqrt(variance + 1e-5);
|
|
2000
|
+
for (let j = 0; j < d_model; j++) {
|
|
2001
|
+
const norm_val = (inBuf[offset + j] - mean) * inv_std;
|
|
2002
|
+
outBuf[offset + j] = norm_val * wBuf[j] + bBuf[j];
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// js/ops/conv2D.js
|
|
2008
|
+
function _cpuConv2D(node) {
|
|
2009
|
+
const input = node.inputs.input;
|
|
2010
|
+
const weight = node.inputs.weight;
|
|
2011
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
2012
|
+
const output = node.outputs.out;
|
|
2013
|
+
const [batch, in_h, in_w, in_c] = input.shape;
|
|
2014
|
+
const [k_h, k_w] = weight.shape;
|
|
2015
|
+
const out_c = output.shape[3];
|
|
2016
|
+
const out_h = output.shape[1];
|
|
2017
|
+
const out_w = output.shape[2];
|
|
2018
|
+
const [stride_y, stride_x] = _pair(node.params.stride, 1);
|
|
2019
|
+
const [pad_y, pad_x] = _pair(node.params.padding, 0);
|
|
2020
|
+
const pads = node.params.pads || [pad_y, pad_x, pad_y, pad_x];
|
|
2021
|
+
const [dil_y, dil_x] = _pair(node.params.dilation, 1);
|
|
2022
|
+
const inBuf = input.buffer;
|
|
2023
|
+
const wBuf = weight.buffer;
|
|
2024
|
+
const outBuf = output.buffer;
|
|
2025
|
+
const groups = node.params.groups || 1;
|
|
2026
|
+
const group_out = out_c / groups;
|
|
2027
|
+
const group_in = weight.shape[2];
|
|
2028
|
+
for (let b = 0; b < batch; b++) {
|
|
2029
|
+
for (let oh = 0; oh < out_h; oh++) {
|
|
2030
|
+
for (let ow = 0; ow < out_w; ow++) {
|
|
2031
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
2032
|
+
let sum = 0;
|
|
2033
|
+
if (groups === in_c) {
|
|
2034
|
+
const mult = out_c / in_c;
|
|
2035
|
+
const ic = Math.floor(oc / mult);
|
|
2036
|
+
const m = oc - ic * mult;
|
|
2037
|
+
for (let kh = 0; kh < k_h; kh++) {
|
|
2038
|
+
for (let kw = 0; kw < k_w; kw++) {
|
|
2039
|
+
const ih = oh * stride_y + kh * dil_y - pads[0];
|
|
2040
|
+
const iw = ow * stride_x + kw * dil_x - pads[1];
|
|
2041
|
+
if (ih >= 0 && ih < in_h && iw >= 0 && iw < in_w) {
|
|
2042
|
+
const in_idx = ((b * in_h + ih) * in_w + iw) * in_c + ic;
|
|
2043
|
+
const w_idx = ((kh * k_w + kw) * in_c + ic) * mult + m;
|
|
2044
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
} else {
|
|
2049
|
+
const g = Math.floor(oc / group_out);
|
|
2050
|
+
const in_start = g * group_in;
|
|
2051
|
+
for (let icl = 0; icl < group_in; icl++) {
|
|
2052
|
+
const ic = in_start + icl;
|
|
2053
|
+
for (let kh = 0; kh < k_h; kh++) {
|
|
2054
|
+
for (let kw = 0; kw < k_w; kw++) {
|
|
2055
|
+
const ih = oh * stride_y + kh * dil_y - pads[0];
|
|
2056
|
+
const iw = ow * stride_x + kw * dil_x - pads[1];
|
|
2057
|
+
if (ih >= 0 && ih < in_h && iw >= 0 && iw < in_w) {
|
|
2058
|
+
const in_idx = ((b * in_h + ih) * in_w + iw) * in_c + ic;
|
|
2059
|
+
const w_idx = ((kh * k_w + kw) * group_in + icl) * out_c + oc;
|
|
2060
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
if (bias) sum += bias[oc];
|
|
2067
|
+
if (node.params.relu === 1 && sum < 0) sum = 0;
|
|
2068
|
+
else if (node.params.relu >= 2) sum = Math.min(Math.max(sum, 0), 6);
|
|
2069
|
+
outBuf[((b * out_h + oh) * out_w + ow) * out_c + oc] = sum;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// js/ops/conv1D.js
|
|
2077
|
+
function _cpuConv1D(node) {
|
|
2078
|
+
const input = node.inputs.input;
|
|
2079
|
+
const weight = node.inputs.weight;
|
|
2080
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
2081
|
+
const output = node.outputs.out;
|
|
2082
|
+
const [in_c, in_l] = input.shape.slice(1);
|
|
2083
|
+
const [out_c, k_c, k] = weight.shape;
|
|
2084
|
+
const out_l = output.shape[2];
|
|
2085
|
+
const stride = _pair(node.params.stride, 1)[0];
|
|
2086
|
+
const padding = _pair(node.params.padding, 0)[0];
|
|
2087
|
+
const relu = node.params.relu;
|
|
2088
|
+
const inBuf = input.buffer;
|
|
2089
|
+
const wBuf = weight.buffer;
|
|
2090
|
+
const outBuf = output.buffer;
|
|
2091
|
+
for (let oc = 0; oc < out_c; oc++) {
|
|
2092
|
+
for (let x = 0; x < out_l; x++) {
|
|
2093
|
+
let sum = bias ? bias[oc] : 0;
|
|
2094
|
+
for (let ic = 0; ic < in_c; ic++) {
|
|
2095
|
+
for (let k_idx = 0; k_idx < k; k_idx++) {
|
|
2096
|
+
const ix = x * stride + k_idx - padding;
|
|
2097
|
+
if (ix >= 0 && ix < in_l) {
|
|
2098
|
+
const in_idx = ic * in_l + ix;
|
|
2099
|
+
const w_idx = (oc * in_c + ic) * k + k_idx;
|
|
2100
|
+
sum += inBuf[in_idx] * wBuf[w_idx];
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
if (relu && sum < 0) sum = 0;
|
|
2105
|
+
outBuf[oc * out_l + x] = sum;
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
// js/ops/matMul.js
|
|
2111
|
+
function _cpuMatMul(node) {
|
|
2112
|
+
const input = node.inputs.input;
|
|
2113
|
+
const weight = node.inputs.weight;
|
|
2114
|
+
const output = node.outputs.out;
|
|
2115
|
+
const M = input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
2116
|
+
const K = input.shape[input.shape.length - 1];
|
|
2117
|
+
const N = output.shape[output.shape.length - 1];
|
|
2118
|
+
const inBuf = input.buffer;
|
|
2119
|
+
const wBuf = weight.buffer;
|
|
2120
|
+
const outBuf = output.buffer;
|
|
2121
|
+
const scale = node.inputs.scale ? node.inputs.scale.buffer : null;
|
|
2122
|
+
const bias = node.inputs.bias ? node.inputs.bias.buffer : null;
|
|
2123
|
+
const w0 = weight.shape.length >= 2 ? weight.shape[0] : N;
|
|
2124
|
+
const w1 = weight.shape.length >= 2 ? weight.shape[1] : K;
|
|
2125
|
+
const doutFirst = scale ? true : node.wLayout ? node.wLayout === "dout" : w0 === N && w1 === K;
|
|
2126
|
+
for (let i = 0; i < M; i++) {
|
|
2127
|
+
for (let j = 0; j < N; j++) {
|
|
2128
|
+
let sum = 0;
|
|
2129
|
+
if (doutFirst) {
|
|
2130
|
+
for (let k = 0; k < K; k++) sum += inBuf[i * K + k] * wBuf[j * K + k];
|
|
2131
|
+
} else {
|
|
2132
|
+
for (let k = 0; k < K; k++) sum += inBuf[i * K + k] * wBuf[k * N + j];
|
|
2133
|
+
}
|
|
2134
|
+
if (scale) sum *= scale[j];
|
|
2135
|
+
if (bias) sum += bias[j];
|
|
2136
|
+
outBuf[i * N + j] = sum;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// js/ops/cast.js
|
|
2142
|
+
function _cpuCast(node) {
|
|
2143
|
+
const input = node.inputs.input || node.inputs.data;
|
|
2144
|
+
const output = node.outputs.out;
|
|
2145
|
+
const inBuf = input.buffer;
|
|
2146
|
+
const outBuf = output.buffer;
|
|
2147
|
+
const to = node.params.to;
|
|
2148
|
+
const intCast = to === "int32" || to === "int64" || to === "int8" || to === 3 || to === 5 || to === 6 || to === 7;
|
|
2149
|
+
const n = Math.min(inBuf.length, outBuf.length);
|
|
2150
|
+
if (intCast) {
|
|
2151
|
+
for (let i = 0; i < n; i++) outBuf[i] = Math.trunc(inBuf[i]);
|
|
2152
|
+
} else {
|
|
2153
|
+
outBuf.set(inBuf.subarray(0, n));
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// js/ops/argMax.js
|
|
2158
|
+
function _cpuArgMax(node) {
|
|
2159
|
+
const input = node.inputs.input || node.inputs.data;
|
|
2160
|
+
const out = node.outputs.out;
|
|
2161
|
+
const inBuf = input.buffer;
|
|
2162
|
+
const outBuf = out.buffer;
|
|
2163
|
+
const shape = input.shape;
|
|
2164
|
+
let axis = node.params.axis !== void 0 ? node.params.axis : 0;
|
|
2165
|
+
if (axis < 0) axis += shape.length;
|
|
2166
|
+
const axisSize = shape[axis];
|
|
2167
|
+
let innerBlock = 1;
|
|
2168
|
+
for (let i = axis + 1; i < shape.length; i++) innerBlock *= shape[i];
|
|
2169
|
+
let outerBlock = 1;
|
|
2170
|
+
for (let i = 0; i < axis; i++) outerBlock *= shape[i];
|
|
2171
|
+
let o = 0;
|
|
2172
|
+
for (let ob = 0; ob < outerBlock; ob++) {
|
|
2173
|
+
for (let ib = 0; ib < innerBlock; ib++) {
|
|
2174
|
+
const base = ob * axisSize * innerBlock + ib;
|
|
2175
|
+
let best = inBuf[base];
|
|
2176
|
+
let bestIdx = 0;
|
|
2177
|
+
for (let a = 1; a < axisSize; a++) {
|
|
2178
|
+
const v = inBuf[base + a * innerBlock];
|
|
2179
|
+
if (v > best) {
|
|
2180
|
+
best = v;
|
|
2181
|
+
bestIdx = a;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
outBuf[o++] = bestIdx;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// js/CPUEngine.js
|
|
2190
|
+
var CPUEngine = class {
|
|
2191
|
+
constructor() {
|
|
2192
|
+
this.tensors = /* @__PURE__ */ new Map();
|
|
2193
|
+
console.log("[VolvoxAI] CPU Fallback Engine ready.");
|
|
2194
|
+
}
|
|
2195
|
+
/**
|
|
2196
|
+
* Allocates CPU memory (ArrayBuffers) for the graph's tensors.
|
|
2197
|
+
*/
|
|
2198
|
+
allocateGraph(graph) {
|
|
2199
|
+
this.graph = graph;
|
|
2200
|
+
for (const [name, tensor] of graph.tensors.entries()) {
|
|
2201
|
+
if (!tensor.buffer) {
|
|
2202
|
+
if (tensor.dtype === "int8") {
|
|
2203
|
+
tensor.buffer = new Int8Array(tensor.sizeBytes);
|
|
2204
|
+
} else {
|
|
2205
|
+
tensor.buffer = new Float32Array(tensor.sizeBytes / 4);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Executes the graph linearly on the CPU.
|
|
2212
|
+
*/
|
|
2213
|
+
async execute(inputsOrGraph, maybeInputs) {
|
|
2214
|
+
const graph = maybeInputs ? inputsOrGraph : this.graph;
|
|
2215
|
+
const inputs = maybeInputs || inputsOrGraph;
|
|
2216
|
+
for (const [name, data] of Object.entries(inputs)) {
|
|
2217
|
+
const tensor = graph.tensors.get(name);
|
|
2218
|
+
if (tensor && tensor.buffer) {
|
|
2219
|
+
tensor.buffer.set(data);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
for (const node of graph.nodes) {
|
|
2223
|
+
this._runNode(node);
|
|
2224
|
+
}
|
|
2225
|
+
const result = {};
|
|
2226
|
+
if (graph.outputNames && graph.outputNames.length > 0) {
|
|
2227
|
+
for (const name of graph.outputNames) {
|
|
2228
|
+
result[name] = graph.tensors.get(name).buffer;
|
|
2229
|
+
}
|
|
2230
|
+
} else {
|
|
2231
|
+
const lastNode = graph.nodes[graph.nodes.length - 1];
|
|
2232
|
+
for (const [key, t] of Object.entries(lastNode.outputs)) {
|
|
2233
|
+
result[t.name] = graph.tensors.get(t.name).buffer;
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
return result;
|
|
2237
|
+
}
|
|
2238
|
+
_runNode(node) {
|
|
2239
|
+
switch (node.opType) {
|
|
2240
|
+
// --- Linear / attention ---
|
|
2241
|
+
case "MatMul":
|
|
2242
|
+
return this._cpuMatMul(node);
|
|
2243
|
+
case "LayerNorm":
|
|
2244
|
+
return this._cpuLayerNorm(node);
|
|
2245
|
+
case "RMSNorm":
|
|
2246
|
+
return this._cpuRMSNorm(node);
|
|
2247
|
+
case "Embedding":
|
|
2248
|
+
return this._cpuEmbedding(node);
|
|
2249
|
+
case "SDPA":
|
|
2250
|
+
return this._cpuSDPA(node);
|
|
2251
|
+
case "CrossSDPA":
|
|
2252
|
+
return this._cpuCrossSDPA(node);
|
|
2253
|
+
case "CrossAttention":
|
|
2254
|
+
return this._cpuCrossAttention(node);
|
|
2255
|
+
// --- Convolution / pooling ---
|
|
2256
|
+
case "Conv2D":
|
|
2257
|
+
return this._cpuConv2D(node);
|
|
2258
|
+
case "Conv1D":
|
|
2259
|
+
return this._cpuConv1D(node);
|
|
2260
|
+
case "ConvTranspose2D":
|
|
2261
|
+
return this._cpuConvTranspose2D(node);
|
|
2262
|
+
case "MaxPool2D":
|
|
2263
|
+
return this._cpuMaxPool2D(node);
|
|
2264
|
+
case "AveragePool":
|
|
2265
|
+
case "AveragePool2D":
|
|
2266
|
+
return this._cpuAveragePool2D(node);
|
|
2267
|
+
case "GlobalAveragePool":
|
|
2268
|
+
return this._cpuGlobalAveragePool(node);
|
|
2269
|
+
case "BatchNorm2D":
|
|
2270
|
+
return this._cpuBatchNorm2D(node);
|
|
2271
|
+
case "ResizeNearest2D":
|
|
2272
|
+
case "Resize":
|
|
2273
|
+
return this._cpuResize(node);
|
|
2274
|
+
case "Upsample2x":
|
|
2275
|
+
case "UpsampleNearest2D":
|
|
2276
|
+
return this._cpuUpsample2x(node);
|
|
2277
|
+
case "Interp1D":
|
|
2278
|
+
case "InterpLinear1D":
|
|
2279
|
+
return this._cpuInterp1D(node);
|
|
2280
|
+
// --- Activations ---
|
|
2281
|
+
case "ReLU":
|
|
2282
|
+
return this._cpuReLU(node);
|
|
2283
|
+
case "LeakyReLU":
|
|
2284
|
+
return this._cpuLeakyReLU(node);
|
|
2285
|
+
case "PReLU":
|
|
2286
|
+
return this._cpuPReLU(node);
|
|
2287
|
+
case "GELU":
|
|
2288
|
+
return this._cpuGELU(node);
|
|
2289
|
+
case "SiLU":
|
|
2290
|
+
case "Swish":
|
|
2291
|
+
return this._cpuSiLU(node);
|
|
2292
|
+
case "Sigmoid":
|
|
2293
|
+
return this._cpuSigmoid(node);
|
|
2294
|
+
case "HardSwish":
|
|
2295
|
+
return this._cpuHardSwish(node);
|
|
2296
|
+
case "HardSigmoid":
|
|
2297
|
+
return this._cpuHardSigmoid(node);
|
|
2298
|
+
case "Tanh":
|
|
2299
|
+
return this._cpuTanh(node);
|
|
2300
|
+
case "Clip":
|
|
2301
|
+
return this._cpuClip(node);
|
|
2302
|
+
// --- Elementwise / reduction ---
|
|
2303
|
+
case "Add":
|
|
2304
|
+
return this._cpuAdd(node);
|
|
2305
|
+
case "Mul":
|
|
2306
|
+
return this._cpuMul(node);
|
|
2307
|
+
case "Sub":
|
|
2308
|
+
return this._cpuSub(node);
|
|
2309
|
+
case "Div":
|
|
2310
|
+
return this._cpuDiv(node);
|
|
2311
|
+
case "Softmax":
|
|
2312
|
+
return this._cpuSoftmax(node);
|
|
2313
|
+
case "LogSoftmax":
|
|
2314
|
+
return this._cpuLogSoftmax(node);
|
|
2315
|
+
case "ReduceSum":
|
|
2316
|
+
return this._cpuReduceSum(node);
|
|
2317
|
+
case "ReduceMean":
|
|
2318
|
+
return this._cpuReduceMean(node);
|
|
2319
|
+
case "ArgMax":
|
|
2320
|
+
return this._cpuArgMax(node);
|
|
2321
|
+
// --- Shape / gather / misc ---
|
|
2322
|
+
case "Transpose":
|
|
2323
|
+
return this._cpuTranspose(node);
|
|
2324
|
+
case "Concat":
|
|
2325
|
+
case "Concat2":
|
|
2326
|
+
return this._cpuConcat2(node);
|
|
2327
|
+
case "Split":
|
|
2328
|
+
return this._cpuSplit(node);
|
|
2329
|
+
case "Slice":
|
|
2330
|
+
return this._cpuSlice(node);
|
|
2331
|
+
case "Pad":
|
|
2332
|
+
return this._cpuPad(node);
|
|
2333
|
+
case "Expand":
|
|
2334
|
+
case "Broadcast":
|
|
2335
|
+
return this._cpuExpand(node);
|
|
2336
|
+
case "Gather":
|
|
2337
|
+
return this._cpuGather(node);
|
|
2338
|
+
case "GatherElements":
|
|
2339
|
+
return this._cpuGatherElements(node);
|
|
2340
|
+
case "Where":
|
|
2341
|
+
case "Mask":
|
|
2342
|
+
return this._cpuWhere(node);
|
|
2343
|
+
case "Cast":
|
|
2344
|
+
return this._cpuCast(node);
|
|
2345
|
+
case "DequantizeLinear":
|
|
2346
|
+
return this._cpuDequantizeLinear(node);
|
|
2347
|
+
case "NonMaxSuppression":
|
|
2348
|
+
return this._cpuNonMaxSuppression(node);
|
|
2349
|
+
case "SpatialSoftargmaxY":
|
|
2350
|
+
return this._cpuSpatialSoftargmaxY(node);
|
|
2351
|
+
case "ProfileX":
|
|
2352
|
+
return this._cpuProfileX(node);
|
|
2353
|
+
case "ProfileY":
|
|
2354
|
+
return this._cpuProfileY(node);
|
|
2355
|
+
case "MeanHeight":
|
|
2356
|
+
return this._cpuMeanHeight(node);
|
|
2357
|
+
// Shape-only ops just copy their data through to the output buffer.
|
|
2358
|
+
case "Reshape":
|
|
2359
|
+
case "Flatten":
|
|
2360
|
+
case "Squeeze":
|
|
2361
|
+
case "Unsqueeze":
|
|
2362
|
+
case "Dropout":
|
|
2363
|
+
case "Identity":
|
|
2364
|
+
return this._cpuReshape(node);
|
|
2365
|
+
default:
|
|
2366
|
+
console.warn(`[VolvoxAI CPU] Executing ${node.opType} is not implemented; node ${node.id} skipped.`);
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
};
|
|
2370
|
+
CPUEngine.prototype._cpuWhere = _cpuWhere;
|
|
2371
|
+
CPUEngine.prototype._cpuPad = _cpuPad;
|
|
2372
|
+
CPUEngine.prototype._cpuAveragePool2D = _cpuAveragePool2D;
|
|
2373
|
+
CPUEngine.prototype._cpuSlice = _cpuSlice;
|
|
2374
|
+
CPUEngine.prototype._cpuConvTranspose2D = _cpuConvTranspose2D;
|
|
2375
|
+
CPUEngine.prototype._cpuReduceSum = _cpuReduceSum;
|
|
2376
|
+
CPUEngine.prototype._cpuReduceMean = _cpuReduceMean;
|
|
2377
|
+
CPUEngine.prototype._cpuBatchNorm2D = _cpuBatchNorm2D;
|
|
2378
|
+
CPUEngine.prototype._cpuSoftmax = _cpuSoftmax;
|
|
2379
|
+
CPUEngine.prototype._cpuLogSoftmax = _cpuLogSoftmax;
|
|
2380
|
+
CPUEngine.prototype._cpuSub = _cpuSub;
|
|
2381
|
+
CPUEngine.prototype._cpuSiLU = _cpuSiLU;
|
|
2382
|
+
CPUEngine.prototype._cpuRMSNorm = _cpuRMSNorm;
|
|
2383
|
+
CPUEngine.prototype._cpuTanh = _cpuTanh;
|
|
2384
|
+
CPUEngine.prototype._cpuDequantizeLinear = _cpuDequantizeLinear;
|
|
2385
|
+
CPUEngine.prototype._cpuExpand = _cpuExpand;
|
|
2386
|
+
CPUEngine.prototype._cpuPReLU = _cpuPReLU;
|
|
2387
|
+
CPUEngine.prototype._cpuDiv = _cpuDiv;
|
|
2388
|
+
CPUEngine.prototype._cpuNonMaxSuppression = _cpuNonMaxSuppression;
|
|
2389
|
+
CPUEngine.prototype._cpuGather = _cpuGather;
|
|
2390
|
+
CPUEngine.prototype._cpuGatherElements = _cpuGatherElements;
|
|
2391
|
+
CPUEngine.prototype._cpuCrossAttention = _cpuCrossAttention;
|
|
2392
|
+
CPUEngine.prototype._cpuSpatialSoftargmaxY = _cpuSpatialSoftargmaxY;
|
|
2393
|
+
CPUEngine.prototype._cpuTranspose = _cpuTranspose;
|
|
2394
|
+
CPUEngine.prototype._cpuCrossSDPA = _cpuCrossSDPA;
|
|
2395
|
+
CPUEngine.prototype._cpuMeanHeight = _cpuMeanHeight;
|
|
2396
|
+
CPUEngine.prototype._cpuMaxPool2D = _cpuMaxPool2D;
|
|
2397
|
+
CPUEngine.prototype._cpuInterp1D = _cpuInterp1D;
|
|
2398
|
+
CPUEngine.prototype._cpuProfileX = _cpuProfileX;
|
|
2399
|
+
CPUEngine.prototype._cpuProfileY = _cpuProfileY;
|
|
2400
|
+
CPUEngine.prototype._cpuConcat2 = _cpuConcat2;
|
|
2401
|
+
CPUEngine.prototype._cpuUpsample2x = _cpuUpsample2x;
|
|
2402
|
+
CPUEngine.prototype._cpuSDPA = _cpuSDPA;
|
|
2403
|
+
CPUEngine.prototype._cpuEmbedding = _cpuEmbedding;
|
|
2404
|
+
CPUEngine.prototype._cpuMul = _cpuMul;
|
|
2405
|
+
CPUEngine.prototype._cpuAdd = _cpuAdd;
|
|
2406
|
+
CPUEngine.prototype._cpuGlobalAveragePool = _cpuGlobalAveragePool;
|
|
2407
|
+
CPUEngine.prototype._cpuClip = _cpuClip;
|
|
2408
|
+
CPUEngine.prototype._cpuReshape = _cpuReshape;
|
|
2409
|
+
CPUEngine.prototype._cpuSplit = _cpuSplit;
|
|
2410
|
+
CPUEngine.prototype._cpuResize = _cpuResize;
|
|
2411
|
+
CPUEngine.prototype._cpuSigmoid = _cpuSigmoid;
|
|
2412
|
+
CPUEngine.prototype._cpuHardSigmoid = _cpuHardSigmoid;
|
|
2413
|
+
CPUEngine.prototype._cpuHardSwish = _cpuHardSwish;
|
|
2414
|
+
CPUEngine.prototype._cpuReLU = _cpuReLU;
|
|
2415
|
+
CPUEngine.prototype._cpuLeakyReLU = _cpuLeakyReLU;
|
|
2416
|
+
CPUEngine.prototype._cpuGELU = _cpuGELU;
|
|
2417
|
+
CPUEngine.prototype._cpuLayerNorm = _cpuLayerNorm;
|
|
2418
|
+
CPUEngine.prototype._cpuConv2D = _cpuConv2D;
|
|
2419
|
+
CPUEngine.prototype._cpuConv1D = _cpuConv1D;
|
|
2420
|
+
CPUEngine.prototype._cpuMatMul = _cpuMatMul;
|
|
2421
|
+
CPUEngine.prototype._cpuCast = _cpuCast;
|
|
2422
|
+
CPUEngine.prototype._cpuArgMax = _cpuArgMax;
|
|
2423
|
+
|
|
2424
|
+
// js/WasmEngine.js
|
|
2425
|
+
var WasmEngine = class _WasmEngine extends CPUEngine {
|
|
2426
|
+
constructor(wasmModule) {
|
|
2427
|
+
super();
|
|
2428
|
+
this.wasmModule = wasmModule;
|
|
2429
|
+
this.api = wasmModule.instance.exports;
|
|
2430
|
+
this.mem = this.api.memory;
|
|
2431
|
+
this.pointers = /* @__PURE__ */ new Map();
|
|
2432
|
+
console.log("[VolvoxAI] WASM Engine ready (Tier 2 Fallback).");
|
|
2433
|
+
}
|
|
2434
|
+
static async init(wasmUrl) {
|
|
2435
|
+
try {
|
|
2436
|
+
let buffer;
|
|
2437
|
+
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
2438
|
+
const fs = await import("fs");
|
|
2439
|
+
let url = wasmUrl;
|
|
2440
|
+
if (!url.startsWith("/")) url = "./" + url;
|
|
2441
|
+
buffer = fs.readFileSync(url);
|
|
2442
|
+
} else {
|
|
2443
|
+
const response = await fetch(wasmUrl);
|
|
2444
|
+
if (!response.ok) throw new Error("WASM file not found.");
|
|
2445
|
+
buffer = await response.arrayBuffer();
|
|
2446
|
+
}
|
|
2447
|
+
const env = { expf: Math.exp, logf: Math.log, powf: Math.pow };
|
|
2448
|
+
const module = await WebAssembly.instantiate(buffer, { env, math: env });
|
|
2449
|
+
return new _WasmEngine(module);
|
|
2450
|
+
} catch (e) {
|
|
2451
|
+
console.warn(`[VolvoxAI] Failed to load WASM from ${wasmUrl}:`, e);
|
|
2452
|
+
return null;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
createGraph() {
|
|
2456
|
+
return new Graph();
|
|
2457
|
+
}
|
|
2458
|
+
_alloc(tensor) {
|
|
2459
|
+
if (!this.pointers.has(tensor.name)) {
|
|
2460
|
+
const ptr = this.api.alloc_bytes(tensor.sizeBytes);
|
|
2461
|
+
const needed = ptr + tensor.sizeBytes;
|
|
2462
|
+
const currentBytes = this.mem.buffer.byteLength;
|
|
2463
|
+
if (needed > currentBytes) {
|
|
2464
|
+
const pagesNeeded = Math.ceil((needed - currentBytes) / 65536);
|
|
2465
|
+
this.mem.grow(pagesNeeded);
|
|
2466
|
+
}
|
|
2467
|
+
this.pointers.set(tensor.name, ptr);
|
|
2468
|
+
const wasmView = new Float32Array(this.mem.buffer, ptr, tensor.sizeBytes / 4);
|
|
2469
|
+
if (tensor.isWeight && tensor.buffer) {
|
|
2470
|
+
let view = tensor.buffer;
|
|
2471
|
+
const dw = this._doutWeights && this._doutWeights.get(tensor.name);
|
|
2472
|
+
if (dw) {
|
|
2473
|
+
const { din, dout } = dw;
|
|
2474
|
+
const t = new Float32Array(din * dout);
|
|
2475
|
+
for (let j = 0; j < dout; j++) for (let k = 0; k < din; k++) t[k * dout + j] = view[j * din + k];
|
|
2476
|
+
view = t;
|
|
2477
|
+
}
|
|
2478
|
+
const src = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
2479
|
+
const bytesToCopy = Math.min(view.byteLength, tensor.sizeBytes);
|
|
2480
|
+
new Uint8Array(this.mem.buffer, ptr, bytesToCopy).set(src.subarray(0, bytesToCopy));
|
|
2481
|
+
}
|
|
2482
|
+
tensor.buffer = wasmView;
|
|
2483
|
+
}
|
|
2484
|
+
return this.pointers.get(tensor.name);
|
|
2485
|
+
}
|
|
2486
|
+
allocateGraph(graph) {
|
|
2487
|
+
return this.compile(graph);
|
|
2488
|
+
}
|
|
2489
|
+
compile(graph) {
|
|
2490
|
+
console.log("[VolvoxAI WASM] Allocating graph tensors on WASM heap...");
|
|
2491
|
+
if (this.api.reset_heap) this.api.reset_heap();
|
|
2492
|
+
this.pointers.clear();
|
|
2493
|
+
this._doutWeights = /* @__PURE__ */ new Map();
|
|
2494
|
+
for (const n of graph.nodes) {
|
|
2495
|
+
if ((n.opType === "MatMul" || n.opType === "Linear" || n.opType === "Gemm") && n.wLayout === "dout" && n.inputs.weight && !n.inputs.scale) {
|
|
2496
|
+
const din = n.inputs.input.shape[n.inputs.input.shape.length - 1];
|
|
2497
|
+
const dout = n.outputs.out.shape[n.outputs.out.shape.length - 1];
|
|
2498
|
+
this._doutWeights.set(n.inputs.weight.name, { din, dout });
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
for (const [name, tensor] of graph.tensors.entries()) {
|
|
2502
|
+
this._alloc(tensor);
|
|
2503
|
+
}
|
|
2504
|
+
for (const [name, tensor] of graph.tensors.entries()) {
|
|
2505
|
+
const ptr = this.pointers.get(name);
|
|
2506
|
+
tensor.buffer = new Float32Array(this.mem.buffer, ptr, tensor.sizeBytes / 4);
|
|
2507
|
+
}
|
|
2508
|
+
this.graph = graph;
|
|
2509
|
+
return this;
|
|
2510
|
+
}
|
|
2511
|
+
async execute(inputs) {
|
|
2512
|
+
for (const [name, data] of Object.entries(inputs)) {
|
|
2513
|
+
const tensor = this.graph.tensors.get(name);
|
|
2514
|
+
if (!tensor) continue;
|
|
2515
|
+
const ptr = this.pointers.get(name);
|
|
2516
|
+
new Float32Array(this.mem.buffer, ptr, data.length).set(data);
|
|
2517
|
+
}
|
|
2518
|
+
for (const node of this.graph.nodes) {
|
|
2519
|
+
try {
|
|
2520
|
+
const inPtr = this.pointers.get(node.inputs.input?.name);
|
|
2521
|
+
const outPtr = this.pointers.get(node.outputs.out?.name);
|
|
2522
|
+
const wPtr = this.pointers.get(node.inputs.weight?.name);
|
|
2523
|
+
const bPtr = this.pointers.get(node.inputs.bias?.name);
|
|
2524
|
+
if (node.opType === "Conv2D") {
|
|
2525
|
+
const wPtr2 = this.pointers.get(node.inputs.weight.name);
|
|
2526
|
+
const bPtr2 = node.inputs.bias ? this.pointers.get(node.inputs.bias.name) : 0;
|
|
2527
|
+
const inShape = node.inputs.input.shape;
|
|
2528
|
+
const outShape = node.outputs.out.shape;
|
|
2529
|
+
const wShape = node.inputs.weight.shape;
|
|
2530
|
+
const [sy, sx] = _pair(node.params.stride, 1);
|
|
2531
|
+
const [dy, dx] = _pair(node.params.dilation, 1);
|
|
2532
|
+
const padPair = _pair(node.params.padding, 0);
|
|
2533
|
+
const pads = node.params.pads || [padPair[0], padPair[1], padPair[0], padPair[1]];
|
|
2534
|
+
const groups = node.params.groups || 1;
|
|
2535
|
+
const relu = node.params.relu ? 1 : 0;
|
|
2536
|
+
this.api.conv2d_f32(
|
|
2537
|
+
inPtr,
|
|
2538
|
+
outPtr,
|
|
2539
|
+
wPtr2,
|
|
2540
|
+
bPtr2,
|
|
2541
|
+
inShape[0],
|
|
2542
|
+
inShape[1],
|
|
2543
|
+
inShape[2],
|
|
2544
|
+
inShape[3],
|
|
2545
|
+
wShape[0],
|
|
2546
|
+
wShape[1],
|
|
2547
|
+
wShape[2],
|
|
2548
|
+
wShape[3],
|
|
2549
|
+
outShape[1],
|
|
2550
|
+
outShape[2],
|
|
2551
|
+
sy,
|
|
2552
|
+
sx,
|
|
2553
|
+
pads[0],
|
|
2554
|
+
pads[1],
|
|
2555
|
+
groups,
|
|
2556
|
+
relu,
|
|
2557
|
+
dy,
|
|
2558
|
+
dx
|
|
2559
|
+
);
|
|
2560
|
+
} else if (node.opType === "ConvTranspose2D") {
|
|
2561
|
+
const wPtr2 = this.pointers.get(node.inputs.weight.name);
|
|
2562
|
+
const bPtr2 = node.inputs.bias ? this.pointers.get(node.inputs.bias.name) : 0;
|
|
2563
|
+
const [b, in_h, in_w, in_c] = node.inputs.input.shape;
|
|
2564
|
+
const [out_b, out_h, out_w, out_c] = node.outputs.out.shape;
|
|
2565
|
+
const kh = node.params.kernel[0], kw = node.params.kernel[1];
|
|
2566
|
+
const sh = node.params.stride ? node.params.stride[0] : 1;
|
|
2567
|
+
const sw = node.params.stride ? node.params.stride[1] : 1;
|
|
2568
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
2569
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
2570
|
+
this.api.conv_transpose2d_f32(
|
|
2571
|
+
inPtr,
|
|
2572
|
+
wPtr2,
|
|
2573
|
+
bPtr2,
|
|
2574
|
+
outPtr,
|
|
2575
|
+
b,
|
|
2576
|
+
in_h,
|
|
2577
|
+
in_w,
|
|
2578
|
+
in_c,
|
|
2579
|
+
out_h,
|
|
2580
|
+
out_w,
|
|
2581
|
+
out_c,
|
|
2582
|
+
kh,
|
|
2583
|
+
kw,
|
|
2584
|
+
sh,
|
|
2585
|
+
sw,
|
|
2586
|
+
ph,
|
|
2587
|
+
pw
|
|
2588
|
+
);
|
|
2589
|
+
} else if (node.opType === "ReduceSum") {
|
|
2590
|
+
const inShape = node.inputs.input ? node.inputs.input.shape : node.inputs.data.shape;
|
|
2591
|
+
const shape = inShape.length === 2 ? inShape : [1, node.inputs.input ? node.inputs.input.buffer.length : node.inputs.data.buffer.length];
|
|
2592
|
+
this.api.reduce_sum_f32(inPtr, outPtr, shape[0], shape[1]);
|
|
2593
|
+
} else if (node.opType === "ReduceMean") {
|
|
2594
|
+
const inShape = node.inputs.input ? node.inputs.input.shape : node.inputs.data.shape;
|
|
2595
|
+
const shape = inShape.length === 2 ? inShape : [1, node.inputs.input ? node.inputs.input.buffer.length : node.inputs.data.buffer.length];
|
|
2596
|
+
this.api.reduce_mean_f32(inPtr, outPtr, shape[0], shape[1]);
|
|
2597
|
+
} else if (node.opType === "MatMul") {
|
|
2598
|
+
const wPtr2 = this.pointers.get(node.inputs.weight.name);
|
|
2599
|
+
const bPtr2 = node.inputs.bias ? this.pointers.get(node.inputs.bias.name) : 0;
|
|
2600
|
+
const d_in = node.inputs.input.shape[node.inputs.input.shape.length - 1];
|
|
2601
|
+
const d_out = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
2602
|
+
const flatSeq = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
2603
|
+
if (node.inputs.scale) {
|
|
2604
|
+
const sPtr = this.pointers.get(node.inputs.scale.name);
|
|
2605
|
+
this.api.matmul_int8_f32(inPtr, wPtr2, sPtr, bPtr2, outPtr, flatSeq, d_in, d_out);
|
|
2606
|
+
} else {
|
|
2607
|
+
this.api.matmul_f32(inPtr, wPtr2, bPtr2, outPtr, flatSeq, d_in, d_out);
|
|
2608
|
+
}
|
|
2609
|
+
} else if (node.opType === "LayerNorm") {
|
|
2610
|
+
const flatSeq = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
2611
|
+
const d_model = node.params.d_model;
|
|
2612
|
+
this.api.layernorm_f32(inPtr, wPtr, bPtr, outPtr, flatSeq, d_model, 1e-5);
|
|
2613
|
+
} else if (node.opType === "SDPA") {
|
|
2614
|
+
const qkvPtr = this.pointers.get(node.inputs.qkv.name);
|
|
2615
|
+
const seqLen = node.inputs.qkv.shape[1];
|
|
2616
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
2617
|
+
const heads = node.params.heads || node.params.num_heads || 8;
|
|
2618
|
+
const head_dim = node.params.head_dim || d_model / heads;
|
|
2619
|
+
const scale = node.params.scale !== void 0 ? node.params.scale : 1 / Math.sqrt(head_dim);
|
|
2620
|
+
this.api.sdpa_f32(qkvPtr, outPtr, seqLen, d_model, heads, head_dim, scale);
|
|
2621
|
+
} else if (node.opType === "CrossSDPA") {
|
|
2622
|
+
const qPtr = this.pointers.get(node.inputs.q.name);
|
|
2623
|
+
const kPtr = this.pointers.get(node.inputs.k.name);
|
|
2624
|
+
const vPtr = this.pointers.get(node.inputs.v.name);
|
|
2625
|
+
const seqQ = node.inputs.q.shape[1];
|
|
2626
|
+
const seqKV = node.inputs.k.shape[1];
|
|
2627
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
2628
|
+
const heads = node.params.heads || node.params.num_heads || 8;
|
|
2629
|
+
const head_dim = node.params.head_dim || d_model / heads;
|
|
2630
|
+
this.api.cross_sdpa_f32(qPtr, kPtr, vPtr, outPtr, seqQ, seqKV, d_model, heads, head_dim, 1 / Math.sqrt(head_dim));
|
|
2631
|
+
} else if (node.opType === "Embedding") {
|
|
2632
|
+
const seqLen = node.inputs.input.shape.reduce((a, b) => a * b, 1);
|
|
2633
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
2634
|
+
this.api.embedding_f32(inPtr, wPtr, outPtr, seqLen, d_model);
|
|
2635
|
+
} else if (node.opType === "ReLU") {
|
|
2636
|
+
const inShape = node.inputs.input.shape;
|
|
2637
|
+
const elements = inShape.reduce((a, b) => a * b, 1);
|
|
2638
|
+
this.api.relu_f32(inPtr, outPtr, elements);
|
|
2639
|
+
} else if (node.opType === "GELU") {
|
|
2640
|
+
const elements = node.inputs.input.sizeBytes / 4;
|
|
2641
|
+
this.api.gelu_f32(inPtr, outPtr, elements);
|
|
2642
|
+
} else if (node.opType === "Add") {
|
|
2643
|
+
this._cpuAdd(node);
|
|
2644
|
+
} else if (node.opType === "Mul") {
|
|
2645
|
+
this._cpuMul(node);
|
|
2646
|
+
} else if (node.opType === "Conv1D") {
|
|
2647
|
+
const inShape = node.inputs.input.shape;
|
|
2648
|
+
const wShape = node.inputs.weight.shape;
|
|
2649
|
+
const [st] = _pair(node.params.stride, 1);
|
|
2650
|
+
const [pd] = _pair(node.params.padding, 0);
|
|
2651
|
+
const groups = node.params.groups || 1;
|
|
2652
|
+
const relu = node.params.relu ? 1 : 0;
|
|
2653
|
+
this.api.conv1d_f32(
|
|
2654
|
+
inPtr,
|
|
2655
|
+
outPtr,
|
|
2656
|
+
wPtr,
|
|
2657
|
+
bPtr,
|
|
2658
|
+
inShape[1],
|
|
2659
|
+
inShape[2],
|
|
2660
|
+
wShape[0],
|
|
2661
|
+
wShape[1],
|
|
2662
|
+
wShape[2],
|
|
2663
|
+
st,
|
|
2664
|
+
pd,
|
|
2665
|
+
groups,
|
|
2666
|
+
relu
|
|
2667
|
+
);
|
|
2668
|
+
} else if (node.opType === "UpsampleNearest2D") {
|
|
2669
|
+
const inShape = node.inputs.input.shape;
|
|
2670
|
+
this.api.upsample_nearest2x_f32(inPtr, outPtr, inShape[3], inShape[1], inShape[2]);
|
|
2671
|
+
} else if (node.opType === "Concat" || node.opType === "Concat2") {
|
|
2672
|
+
this._cpuConcat2(node);
|
|
2673
|
+
} else if (node.opType === "ProfileY") {
|
|
2674
|
+
const inShape = node.inputs.input.shape;
|
|
2675
|
+
this.api.profile_y_f32(inPtr, outPtr, inShape[3], inShape[1], inShape[2]);
|
|
2676
|
+
} else if (node.opType === "ProfileX") {
|
|
2677
|
+
const inShape = node.inputs.input.shape;
|
|
2678
|
+
this.api.profile_x_f32(inPtr, outPtr, inShape[3], inShape[1], inShape[2]);
|
|
2679
|
+
} else if (node.opType === "InterpLinear1D") {
|
|
2680
|
+
const inShape = node.inputs.input.shape;
|
|
2681
|
+
const outL = node.params.size;
|
|
2682
|
+
this.api.interp1d_f32(inPtr, outPtr, inShape[1], inShape[2], outL);
|
|
2683
|
+
} else if (node.opType === "SpatialSoftargmaxY") {
|
|
2684
|
+
const inShape = node.inputs.input.shape;
|
|
2685
|
+
this.api.spatial_softargmax_y_f32(inPtr, outPtr, inShape[3], inShape[1], inShape[2]);
|
|
2686
|
+
} else if (node.opType === "Sigmoid") {
|
|
2687
|
+
const inS = node.inputs.input.shape;
|
|
2688
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2689
|
+
this.api.sigmoid_f32(inPtr, outPtr, elements);
|
|
2690
|
+
} else if (node.opType === "Clip") {
|
|
2691
|
+
let minVal = node.params.min !== void 0 ? node.params.min : -1e9;
|
|
2692
|
+
let maxVal = node.params.max !== void 0 ? node.params.max : 1e9;
|
|
2693
|
+
if (node.inputs.min) {
|
|
2694
|
+
const p = this.pointers.get(node.inputs.min.name);
|
|
2695
|
+
minVal = new Float32Array(this.mem.buffer, p, 1)[0];
|
|
2696
|
+
}
|
|
2697
|
+
if (node.inputs.max) {
|
|
2698
|
+
const p = this.pointers.get(node.inputs.max.name);
|
|
2699
|
+
maxVal = new Float32Array(this.mem.buffer, p, 1)[0];
|
|
2700
|
+
}
|
|
2701
|
+
const inS = node.inputs.input.shape;
|
|
2702
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2703
|
+
this.api.clip_f32(inPtr, outPtr, elements, minVal, maxVal);
|
|
2704
|
+
} else if (node.opType === "HardSwish") {
|
|
2705
|
+
const inS = node.inputs.input.shape;
|
|
2706
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2707
|
+
this.api.hardswish_f32(inPtr, outPtr, elements);
|
|
2708
|
+
} else if (node.opType === "LeakyReLU") {
|
|
2709
|
+
const inS = node.inputs.input.shape;
|
|
2710
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2711
|
+
const alpha = node.params.alpha || 0.01;
|
|
2712
|
+
if (this.api.leakyrelu_f32) this.api.leakyrelu_f32(inPtr, outPtr, elements, alpha);
|
|
2713
|
+
else this._cpuLeakyReLU(node);
|
|
2714
|
+
} else if (node.opType === "PReLU") {
|
|
2715
|
+
if (this.api.prelu_f32) {
|
|
2716
|
+
const inS = node.inputs.input.shape;
|
|
2717
|
+
const wPtr2 = this.pointers.get(node.inputs.weight.name);
|
|
2718
|
+
this.api.prelu_f32(inPtr, wPtr2, outPtr, inS[0], inS[1], inS[2], inS[3]);
|
|
2719
|
+
} else {
|
|
2720
|
+
this._cpuPReLU(node);
|
|
2721
|
+
}
|
|
2722
|
+
} else if (node.opType === "HardSigmoid") {
|
|
2723
|
+
const inS = node.inputs.input.shape;
|
|
2724
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2725
|
+
this.api.hardsigmoid_f32(inPtr, outPtr, elements);
|
|
2726
|
+
} else if (node.opType === "Reshape") {
|
|
2727
|
+
const inS = node.inputs.input.shape;
|
|
2728
|
+
const elements = inS.reduce((a, b) => a * b, 1);
|
|
2729
|
+
this.api.copy_f32(inPtr, outPtr, elements);
|
|
2730
|
+
} else if (node.opType === "Transpose") {
|
|
2731
|
+
this._cpuTranspose(node);
|
|
2732
|
+
} else if (node.opType === "GlobalAveragePool") {
|
|
2733
|
+
const inShape = node.inputs.input.shape;
|
|
2734
|
+
this.api.global_average_pool_f32(inPtr, outPtr, inShape[0], inShape[1], inShape[2], inShape[3]);
|
|
2735
|
+
} else if (node.opType === "BatchNorm2D") {
|
|
2736
|
+
const wPtr2 = this.pointers.get(node.inputs.weight.name);
|
|
2737
|
+
const bPtr2 = this.pointers.get(node.inputs.bias.name);
|
|
2738
|
+
const rmPtr = this.pointers.get(node.inputs.running_mean.name);
|
|
2739
|
+
const rvPtr = this.pointers.get(node.inputs.running_var.name);
|
|
2740
|
+
const [b, h, w, c] = node.inputs.input.shape;
|
|
2741
|
+
const eps = node.params.eps || 1e-5;
|
|
2742
|
+
this.api.batch_norm2d_f32(inPtr, wPtr2, bPtr2, rmPtr, rvPtr, outPtr, b, h, w, c, eps);
|
|
2743
|
+
} else if (node.opType === "ResizeNearest2D") {
|
|
2744
|
+
this._cpuResize(node);
|
|
2745
|
+
} else if (node.opType === "Resize") {
|
|
2746
|
+
const [b, in_h, in_w, c] = node.inputs.input.shape;
|
|
2747
|
+
const [ob, out_h, out_w] = node.outputs.out.shape;
|
|
2748
|
+
this.api.resize_bilinear_f32(inPtr, outPtr, b, in_h, in_w, c, out_h, out_w);
|
|
2749
|
+
} else if (node.opType === "Cast") {
|
|
2750
|
+
this._cpuCast(node);
|
|
2751
|
+
} else if (node.opType === "Slice") {
|
|
2752
|
+
const in_s = [1, 1, 1, 1].slice(0, 4 - node.inputs.input.shape.length).concat(node.inputs.input.shape);
|
|
2753
|
+
const out_s = [1, 1, 1, 1].slice(0, 4 - node.outputs.out.shape.length).concat(node.outputs.out.shape);
|
|
2754
|
+
const starts = node.params.starts || [0, 0, 0, 0];
|
|
2755
|
+
const steps = node.params.steps || [1, 1, 1, 1];
|
|
2756
|
+
const axes = node.params.axes || [0, 1, 2, 3];
|
|
2757
|
+
const st = [0, 0, 0, 0];
|
|
2758
|
+
const sp = [1, 1, 1, 1];
|
|
2759
|
+
for (let i = 0; i < axes.length; i++) {
|
|
2760
|
+
let ax = axes[i];
|
|
2761
|
+
if (ax < 0) ax += node.inputs.input.shape.length;
|
|
2762
|
+
ax += 4 - node.inputs.input.shape.length;
|
|
2763
|
+
st[ax] = starts[i] < 0 ? starts[i] + in_s[ax] : starts[i];
|
|
2764
|
+
sp[ax] = steps[i];
|
|
2765
|
+
}
|
|
2766
|
+
this.api.slice_4d_f32(inPtr, outPtr, in_s[0], in_s[1], in_s[2], in_s[3], out_s[0], out_s[1], out_s[2], out_s[3], st[0], st[1], st[2], st[3], sp[0], sp[1], sp[2], sp[3]);
|
|
2767
|
+
} else if (node.opType === "Split") {
|
|
2768
|
+
this._cpuSplit(node);
|
|
2769
|
+
} else if (node.opType === "Gather") {
|
|
2770
|
+
const in_s = [1, 1, 1, 1].slice(0, 4 - node.inputs.input.shape.length).concat(node.inputs.input.shape);
|
|
2771
|
+
let axis = node.params.axis || 0;
|
|
2772
|
+
if (axis < 0) axis += node.inputs.input.shape.length;
|
|
2773
|
+
axis += 4 - node.inputs.input.shape.length;
|
|
2774
|
+
const idxPtr = this.pointers.get(node.inputs.indices.name);
|
|
2775
|
+
this.api.gather_4d_f32(inPtr, idxPtr, outPtr, in_s[0], in_s[1], in_s[2], in_s[3], node.inputs.indices.sizeBytes / 4, axis);
|
|
2776
|
+
} else if (node.opType === "GatherElements") {
|
|
2777
|
+
this._cpuGatherElements(node);
|
|
2778
|
+
} else if (node.opType === "NonMaxSuppression") {
|
|
2779
|
+
this._cpuNonMaxSuppression(node);
|
|
2780
|
+
} else if (node.opType === "Where" || node.opType === "Mask") {
|
|
2781
|
+
const condPtr = this.pointers.get(node.inputs.cond ? node.inputs.cond.name : node.inputs.condition.name);
|
|
2782
|
+
const aPtr = this.pointers.get(node.inputs.x ? node.inputs.x.name : node.inputs.a.name);
|
|
2783
|
+
const bPtr2 = this.pointers.get(node.inputs.y ? node.inputs.y.name : node.inputs.b.name);
|
|
2784
|
+
this.api.where_f32(condPtr, aPtr, bPtr2, outPtr, node.outputs.out.sizeBytes / 4);
|
|
2785
|
+
} else if (node.opType === "Pad") {
|
|
2786
|
+
const pads = node.params.pads;
|
|
2787
|
+
const pt = pads.length === 8 ? pads[1] : pads[0];
|
|
2788
|
+
const pl = pads.length === 8 ? pads[2] : pads[1];
|
|
2789
|
+
const pb = pads.length === 8 ? pads[5] : pads[2];
|
|
2790
|
+
const pr = pads.length === 8 ? pads[6] : pads[3];
|
|
2791
|
+
const val = node.params.value || 0;
|
|
2792
|
+
const inShape = node.inputs.input ? node.inputs.input.shape : node.inputs.data.shape;
|
|
2793
|
+
const s = inShape.length === 4 ? inShape : [1, inShape[0] || 1, inShape[1] || 1, 1];
|
|
2794
|
+
this.api.pad_2d_f32(inPtr, outPtr, val, s[0], s[1], s[2], s[3], pt, pb, pl, pr);
|
|
2795
|
+
} else if (node.opType === "AveragePool2D" || node.opType === "AveragePool") {
|
|
2796
|
+
const [b, in_h, in_w, c] = (node.inputs.input || node.inputs.x).shape;
|
|
2797
|
+
const [ob, out_h, out_w] = node.outputs.out.shape;
|
|
2798
|
+
const ky = node.params.kernel[0], kx = node.params.kernel[1];
|
|
2799
|
+
const sy = node.params.stride ? node.params.stride[0] : 1;
|
|
2800
|
+
const sx = node.params.stride ? node.params.stride[1] : 1;
|
|
2801
|
+
const py = node.params.padding ? node.params.padding[0] : 0;
|
|
2802
|
+
const px = node.params.padding ? node.params.padding[1] : 0;
|
|
2803
|
+
this.api.averagepool2d_f32(inPtr, outPtr, b, in_h, in_w, c, ky, kx, sy, sx, py, px, out_h, out_w);
|
|
2804
|
+
} else if (node.opType === "Div") {
|
|
2805
|
+
this._cpuDiv(node);
|
|
2806
|
+
} else if (node.opType === "MaxPool2D") {
|
|
2807
|
+
const inShape = node.inputs.input.shape;
|
|
2808
|
+
const outShape = node.outputs.out.shape;
|
|
2809
|
+
const ky = node.params.kernel[0], kx = node.params.kernel[1];
|
|
2810
|
+
const sy = node.params.stride[0], sx = node.params.stride[1];
|
|
2811
|
+
const py = node.params.padding ? node.params.padding[0] : 0;
|
|
2812
|
+
const px = node.params.padding ? node.params.padding[1] : 0;
|
|
2813
|
+
this.api.maxpool2d_f32(inPtr, outPtr, inShape[1], inShape[2], inShape[3], outShape[1], outShape[2], ky, kx, sy, sx, py, px);
|
|
2814
|
+
} else if (node.opType === "MeanHeight") {
|
|
2815
|
+
const inShape = node.inputs.input.shape;
|
|
2816
|
+
this.api.mean_height_f32(inPtr, outPtr, inShape[3], inShape[1], inShape[2]);
|
|
2817
|
+
} else if (node.opType === "Flatten" || node.opType === "Squeeze" || node.opType === "Unsqueeze" || node.opType === "Dropout" || node.opType === "Reshape" || node.opType === "Identity") {
|
|
2818
|
+
this._cpuReshape(node);
|
|
2819
|
+
} else {
|
|
2820
|
+
super._runNode(node);
|
|
2821
|
+
}
|
|
2822
|
+
} catch (e) {
|
|
2823
|
+
console.error("[WasmEngine] Execution failed at node:", node, e);
|
|
2824
|
+
throw e;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
const results = {};
|
|
2828
|
+
for (const name of this.graph.outputNames) {
|
|
2829
|
+
const tensor = this.graph.tensors.get(name);
|
|
2830
|
+
const ptr = this.pointers.get(name);
|
|
2831
|
+
results[name] = new Float32Array(this.mem.buffer, ptr, tensor.sizeBytes / 4).slice();
|
|
2832
|
+
}
|
|
2833
|
+
return results;
|
|
2834
|
+
}
|
|
2835
|
+
};
|
|
2836
|
+
|
|
2837
|
+
// js/GraphExecutor.js
|
|
2838
|
+
var ShaderLibrary2;
|
|
2839
|
+
var GraphExecutor = class {
|
|
2840
|
+
constructor(device, graph) {
|
|
2841
|
+
this.device = device;
|
|
2842
|
+
this.graph = graph;
|
|
2843
|
+
this.pipelines = [];
|
|
2844
|
+
this.gpuBuffers = /* @__PURE__ */ new Map();
|
|
2845
|
+
console.log("[VolvoxAI WebGPU] Starting Graph Compilation...");
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Allocate VRAM for all tensors and compile shaders.
|
|
2849
|
+
*/
|
|
2850
|
+
async compile() {
|
|
2851
|
+
({ ShaderLibrary: ShaderLibrary2 } = await Promise.resolve().then(() => (init_ShaderLibrary(), ShaderLibrary_exports)));
|
|
2852
|
+
this._dinWeights = /* @__PURE__ */ new Map();
|
|
2853
|
+
for (const n of this.graph.nodes) {
|
|
2854
|
+
if ((n.opType === "MatMul" || n.opType === "Linear" || n.opType === "Gemm") && n.wLayout === "din" && n.inputs.weight && !n.inputs.scale) {
|
|
2855
|
+
const din = n.inputs.input.shape[n.inputs.input.shape.length - 1];
|
|
2856
|
+
const dout = n.outputs.out.shape[n.outputs.out.shape.length - 1];
|
|
2857
|
+
this._dinWeights.set(n.inputs.weight.name, { din, dout });
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
this._allocateBuffers();
|
|
2861
|
+
for (const node of this.graph.nodes) {
|
|
2862
|
+
await this._buildNodePipeline(node);
|
|
2863
|
+
}
|
|
2864
|
+
console.log(`[VolvoxAI WebGPU] Compilation complete. Allocated ${this.gpuBuffers.size} VRAM buffers.`);
|
|
2865
|
+
}
|
|
2866
|
+
_allocateBuffers() {
|
|
2867
|
+
for (const [name, tensor] of this.graph.tensors.entries()) {
|
|
2868
|
+
let usage = GPUBufferUsage.STORAGE;
|
|
2869
|
+
if (this.graph.nodes.some((n) => Object.values(n.inputs).some((t) => t.name === name))) {
|
|
2870
|
+
usage |= GPUBufferUsage.COPY_DST;
|
|
2871
|
+
}
|
|
2872
|
+
if (this.graph.nodes.some((n) => Object.values(n.outputs).some((t) => t.name === name))) {
|
|
2873
|
+
usage |= GPUBufferUsage.COPY_SRC;
|
|
2874
|
+
}
|
|
2875
|
+
if (!tensor.isWeight && !this.graph.nodes.some((n) => Object.values(n.outputs).some((t) => t.name === name))) {
|
|
2876
|
+
usage |= GPUBufferUsage.COPY_DST;
|
|
2877
|
+
}
|
|
2878
|
+
if (tensor.isWeight && tensor.buffer) {
|
|
2879
|
+
usage |= GPUBufferUsage.COPY_DST;
|
|
2880
|
+
}
|
|
2881
|
+
const buffer = this.device.createBuffer({
|
|
2882
|
+
label: `Tensor_${name}`,
|
|
2883
|
+
size: Math.ceil(tensor.sizeBytes / 4) * 4,
|
|
2884
|
+
// Align to 4 bytes
|
|
2885
|
+
usage
|
|
2886
|
+
});
|
|
2887
|
+
tensor.gpuBuffer = buffer;
|
|
2888
|
+
this.gpuBuffers.set(name, buffer);
|
|
2889
|
+
if (tensor.isWeight && tensor.buffer) {
|
|
2890
|
+
let wbuf = tensor.buffer;
|
|
2891
|
+
const dw = this._dinWeights && this._dinWeights.get(name);
|
|
2892
|
+
if (dw) {
|
|
2893
|
+
const { din, dout } = dw;
|
|
2894
|
+
const t = new Float32Array(din * dout);
|
|
2895
|
+
for (let k = 0; k < din; k++) for (let j = 0; j < dout; j++) t[j * din + k] = tensor.buffer[k * dout + j];
|
|
2896
|
+
wbuf = t;
|
|
2897
|
+
}
|
|
2898
|
+
const src = new Uint8Array(wbuf.buffer, wbuf.byteOffset, wbuf.byteLength);
|
|
2899
|
+
const padded = Math.ceil(src.byteLength / 4) * 4;
|
|
2900
|
+
if (padded === src.byteLength) {
|
|
2901
|
+
this.device.queue.writeBuffer(buffer, 0, src);
|
|
2902
|
+
} else {
|
|
2903
|
+
const tmp = new Uint8Array(padded);
|
|
2904
|
+
tmp.set(src);
|
|
2905
|
+
this.device.queue.writeBuffer(buffer, 0, tmp);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
async _buildNodePipeline(node) {
|
|
2911
|
+
let wgslCode = "";
|
|
2912
|
+
let fallbackWgslCode = "";
|
|
2913
|
+
let fallbackWorkgroupCount = null;
|
|
2914
|
+
let bindGroupEntries = [];
|
|
2915
|
+
let workgroupCount = [1, 1, 1];
|
|
2916
|
+
if (node.opType === "Conv2D") {
|
|
2917
|
+
wgslCode = ShaderLibrary2.getConv2DShader();
|
|
2918
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
2919
|
+
const weightBuf = node.inputs.weight.gpuBuffer;
|
|
2920
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
2921
|
+
const [n, h, w, c] = node.inputs.input.shape;
|
|
2922
|
+
const [kh, kw] = node.inputs.weight.shape;
|
|
2923
|
+
const outC = node.outputs.out.shape[3];
|
|
2924
|
+
const outH = node.outputs.out.shape[1];
|
|
2925
|
+
const outW = node.outputs.out.shape[2];
|
|
2926
|
+
const [sy, sx] = _pair(node.params.stride, 1);
|
|
2927
|
+
const [pt, pl] = _pair(node.params.padding, 0);
|
|
2928
|
+
const [dy, dx] = _pair(node.params.dilation, 1);
|
|
2929
|
+
const groups = node.params.groups || 1;
|
|
2930
|
+
const pads = Array.isArray(node.params.pads) ? node.params.pads : [pt, pl, pt, pl];
|
|
2931
|
+
const noPad = pads.length >= 4 && pads[0] === 0 && pads[1] === 0 && pads[2] === 0 && pads[3] === 0;
|
|
2932
|
+
const weightLayout = node.params.weight_layout || (groups === c ? "HWCM" : "HWIO");
|
|
2933
|
+
fallbackWgslCode = wgslCode;
|
|
2934
|
+
fallbackWorkgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * outC];
|
|
2935
|
+
if (groups === 1 && weightLayout === "HWIO" && c === 3 && (outC & 15) === 0) {
|
|
2936
|
+
wgslCode = ShaderLibrary2.getConv2DRegularC3Out16Shader();
|
|
2937
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * (outC / 16)];
|
|
2938
|
+
} else if (groups === c && weightLayout === "HWCM" && outC === c && (outC & 7) === 0) {
|
|
2939
|
+
wgslCode = ShaderLibrary2.getConv2DDepthwise8Shader();
|
|
2940
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * Math.ceil(outC / 8)];
|
|
2941
|
+
} else if (groups === 1 && weightLayout === "HWIO" && kh === 1 && kw === 1 && sy === 1 && sx === 1 && noPad && dy === 1 && dx === 1 && outH === h && outW === w) {
|
|
2942
|
+
if ((outC & 15) === 0) {
|
|
2943
|
+
wgslCode = ShaderLibrary2.getConv2DPointwise16TileShader();
|
|
2944
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * (outC / 16)];
|
|
2945
|
+
} else if ((outC & 3) === 0) {
|
|
2946
|
+
wgslCode = ShaderLibrary2.getConv2DPointwise8Vec4Shader();
|
|
2947
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * Math.ceil(outC / 8)];
|
|
2948
|
+
} else if ((outC & 1) === 0) {
|
|
2949
|
+
wgslCode = ShaderLibrary2.getConv2DPointwise8Vec2Shader();
|
|
2950
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), n * Math.ceil(outC / 8)];
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
const biasBuf = this.device.createBuffer({
|
|
2954
|
+
size: Math.ceil(outC * 4 / 4) * 4,
|
|
2955
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
2956
|
+
});
|
|
2957
|
+
if (node.inputs.bias) {
|
|
2958
|
+
this.device.queue.writeBuffer(biasBuf, 0, node.inputs.bias.buffer);
|
|
2959
|
+
}
|
|
2960
|
+
const p = new Uint32Array([
|
|
2961
|
+
n,
|
|
2962
|
+
h,
|
|
2963
|
+
w,
|
|
2964
|
+
c,
|
|
2965
|
+
outC,
|
|
2966
|
+
outH,
|
|
2967
|
+
outW,
|
|
2968
|
+
kh,
|
|
2969
|
+
kw,
|
|
2970
|
+
sy,
|
|
2971
|
+
sx,
|
|
2972
|
+
pt,
|
|
2973
|
+
pl,
|
|
2974
|
+
groups,
|
|
2975
|
+
node.params.relu || 0,
|
|
2976
|
+
dy,
|
|
2977
|
+
dx
|
|
2978
|
+
]);
|
|
2979
|
+
const paramBuf = this.device.createBuffer({ size: Math.ceil(p.byteLength / 16) * 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
2980
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
2981
|
+
bindGroupEntries = [
|
|
2982
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
2983
|
+
{ binding: 1, resource: { buffer: weightBuf } },
|
|
2984
|
+
{ binding: 2, resource: { buffer: biasBuf } },
|
|
2985
|
+
{ binding: 3, resource: { buffer: outputBuf } },
|
|
2986
|
+
{ binding: 4, resource: { buffer: paramBuf } }
|
|
2987
|
+
];
|
|
2988
|
+
if (wgslCode === fallbackWgslCode) workgroupCount = fallbackWorkgroupCount;
|
|
2989
|
+
} else if (node.opType === "Conv1D") {
|
|
2990
|
+
wgslCode = ShaderLibrary2.getConv1DShader();
|
|
2991
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
2992
|
+
const weightBuf = node.inputs.weight.gpuBuffer;
|
|
2993
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
2994
|
+
const biasBuf = this.device.createBuffer({
|
|
2995
|
+
size: Math.ceil(node.inputs.weight.shape[0] * 4 / 4) * 4 || 4,
|
|
2996
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
|
2997
|
+
});
|
|
2998
|
+
if (node.inputs.bias) {
|
|
2999
|
+
this.device.queue.writeBuffer(biasBuf, 0, node.inputs.bias.buffer);
|
|
3000
|
+
}
|
|
3001
|
+
const p = new Uint32Array([
|
|
3002
|
+
node.inputs.input.shape[1],
|
|
3003
|
+
node.inputs.input.shape[2],
|
|
3004
|
+
node.outputs.out.shape[1],
|
|
3005
|
+
node.inputs.weight.shape[2],
|
|
3006
|
+
_pair(node.params.stride, 1)[0],
|
|
3007
|
+
_pair(node.params.padding, 0)[0],
|
|
3008
|
+
node.params.relu ? 1 : 0
|
|
3009
|
+
]);
|
|
3010
|
+
const paramBuf = this.device.createBuffer({ size: p.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3011
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3012
|
+
bindGroupEntries = [
|
|
3013
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3014
|
+
{ binding: 1, resource: { buffer: weightBuf } },
|
|
3015
|
+
{ binding: 2, resource: { buffer: biasBuf } },
|
|
3016
|
+
{ binding: 3, resource: { buffer: outputBuf } },
|
|
3017
|
+
{ binding: 4, resource: { buffer: paramBuf } }
|
|
3018
|
+
];
|
|
3019
|
+
workgroupCount = [Math.ceil(node.outputs.out.shape[2] / 64), node.outputs.out.shape[1], 1];
|
|
3020
|
+
} else if (node.opType === "SpatialSoftargmaxY") {
|
|
3021
|
+
wgslCode = ShaderLibrary2.getSpatialSoftargmaxYShader();
|
|
3022
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
3023
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
3024
|
+
const p = new Uint32Array([
|
|
3025
|
+
node.inputs.input.shape[1],
|
|
3026
|
+
node.inputs.input.shape[2],
|
|
3027
|
+
node.inputs.input.shape[3]
|
|
3028
|
+
]);
|
|
3029
|
+
const paramBuf = this.device.createBuffer({ size: p.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3030
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3031
|
+
bindGroupEntries = [
|
|
3032
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3033
|
+
{ binding: 1, resource: { buffer: outputBuf } },
|
|
3034
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3035
|
+
];
|
|
3036
|
+
workgroupCount = [Math.ceil(node.inputs.input.shape[2] / 64), node.inputs.input.shape[3], 1];
|
|
3037
|
+
} else if (node.opType === "UpsampleNearest2D") {
|
|
3038
|
+
wgslCode = ShaderLibrary2.getUpsample2xShader();
|
|
3039
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
3040
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
3041
|
+
const p = new Uint32Array([
|
|
3042
|
+
node.inputs.input.shape[0],
|
|
3043
|
+
node.inputs.input.shape[1],
|
|
3044
|
+
node.inputs.input.shape[2],
|
|
3045
|
+
node.inputs.input.shape[3]
|
|
3046
|
+
]);
|
|
3047
|
+
const paramBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3048
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3049
|
+
bindGroupEntries = [
|
|
3050
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3051
|
+
{ binding: 1, resource: { buffer: outputBuf } },
|
|
3052
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3053
|
+
];
|
|
3054
|
+
workgroupCount = [
|
|
3055
|
+
Math.ceil(node.outputs.out.shape[2] / 8),
|
|
3056
|
+
Math.ceil(node.outputs.out.shape[1] / 8),
|
|
3057
|
+
node.outputs.out.shape[0] * node.outputs.out.shape[3]
|
|
3058
|
+
];
|
|
3059
|
+
} else if (node.opType === "Concat") {
|
|
3060
|
+
const shaderModule2 = this.device.createShaderModule({ code: ShaderLibrary2.getConcatCopyShader() });
|
|
3061
|
+
const pipeline2 = await this.device.createComputePipelineAsync({
|
|
3062
|
+
layout: "auto",
|
|
3063
|
+
compute: { module: shaderModule2, entryPoint: "main" }
|
|
3064
|
+
});
|
|
3065
|
+
let offset = 0;
|
|
3066
|
+
for (const k of ["input", "a", "b", "c", "d", "e", "f", "g", "h"]) {
|
|
3067
|
+
const t = node.inputs[k];
|
|
3068
|
+
if (!t) continue;
|
|
3069
|
+
const size = t.sizeBytes / 4;
|
|
3070
|
+
const p = new Uint32Array([size, offset]);
|
|
3071
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3072
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3073
|
+
const bindGroup2 = this.device.createBindGroup({
|
|
3074
|
+
layout: pipeline2.getBindGroupLayout(0),
|
|
3075
|
+
entries: [
|
|
3076
|
+
{ binding: 0, resource: { buffer: t.gpuBuffer } },
|
|
3077
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3078
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3079
|
+
]
|
|
3080
|
+
});
|
|
3081
|
+
this.pipelines.push({ pipeline: pipeline2, bindGroup: bindGroup2, workgroupCount: [Math.ceil(size / 64), 1, 1], nodeName: `${node.id}_concat` });
|
|
3082
|
+
offset += size;
|
|
3083
|
+
}
|
|
3084
|
+
return;
|
|
3085
|
+
} else if (node.opType === "ProfileY") {
|
|
3086
|
+
wgslCode = ShaderLibrary2.getProfileYShader();
|
|
3087
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
3088
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
3089
|
+
const p = new Uint32Array([node.inputs.input.shape[1], node.inputs.input.shape[2], node.inputs.input.shape[3]]);
|
|
3090
|
+
const paramBuf = this.device.createBuffer({ size: p.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3091
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3092
|
+
bindGroupEntries = [
|
|
3093
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3094
|
+
{ binding: 1, resource: { buffer: outputBuf } },
|
|
3095
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3096
|
+
];
|
|
3097
|
+
workgroupCount = [Math.ceil(node.inputs.input.shape[1] / 64), node.inputs.input.shape[3], 1];
|
|
3098
|
+
} else if (node.opType === "ProfileX") {
|
|
3099
|
+
wgslCode = ShaderLibrary2.getProfileXShader();
|
|
3100
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
3101
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
3102
|
+
const p = new Uint32Array([node.inputs.input.shape[1], node.inputs.input.shape[2], node.inputs.input.shape[3]]);
|
|
3103
|
+
const paramBuf = this.device.createBuffer({ size: p.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3104
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3105
|
+
bindGroupEntries = [
|
|
3106
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3107
|
+
{ binding: 1, resource: { buffer: outputBuf } },
|
|
3108
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3109
|
+
];
|
|
3110
|
+
workgroupCount = [Math.ceil(node.inputs.input.shape[2] / 64), node.inputs.input.shape[3], 1];
|
|
3111
|
+
} else if (node.opType === "InterpLinear1D") {
|
|
3112
|
+
wgslCode = ShaderLibrary2.getInterp1DShader();
|
|
3113
|
+
const inputBuf = node.inputs.input.gpuBuffer;
|
|
3114
|
+
const outputBuf = node.outputs.out.gpuBuffer;
|
|
3115
|
+
const p = new Uint32Array([node.inputs.input.shape[1], node.inputs.input.shape[2], node.params.size]);
|
|
3116
|
+
const paramBuf = this.device.createBuffer({ size: p.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3117
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3118
|
+
bindGroupEntries = [
|
|
3119
|
+
{ binding: 0, resource: { buffer: inputBuf } },
|
|
3120
|
+
{ binding: 1, resource: { buffer: outputBuf } },
|
|
3121
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3122
|
+
];
|
|
3123
|
+
workgroupCount = [Math.ceil(node.params.size / 64), node.inputs.input.shape[1], 1];
|
|
3124
|
+
} else if (node.opType === "MatMul") {
|
|
3125
|
+
if (node.inputs.scale) {
|
|
3126
|
+
wgslCode = ShaderLibrary2.getLinearInt8Shader();
|
|
3127
|
+
} else {
|
|
3128
|
+
wgslCode = ShaderLibrary2.getLinearF32Shader();
|
|
3129
|
+
}
|
|
3130
|
+
const dummyBias = this.device.createBuffer({ size: 4096, usage: GPUBufferUsage.STORAGE });
|
|
3131
|
+
const seq_len = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
3132
|
+
const d_in = node.inputs.input.shape[node.inputs.input.shape.length - 1];
|
|
3133
|
+
const d_out = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
3134
|
+
const p = new Uint32Array([seq_len, d_in, d_out]);
|
|
3135
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3136
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3137
|
+
const biasBuf = node.inputs.bias ? node.inputs.bias.gpuBuffer : dummyBias;
|
|
3138
|
+
bindGroupEntries = [
|
|
3139
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3140
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3141
|
+
// binding 2 (scale) exists only in the int8 shader; the f32 shader's
|
|
3142
|
+
// unused binding 2 is dropped by layout:"auto", so we must omit it.
|
|
3143
|
+
...node.inputs.scale ? [{ binding: 2, resource: { buffer: node.inputs.scale.gpuBuffer } }] : [],
|
|
3144
|
+
{ binding: 3, resource: { buffer: biasBuf } },
|
|
3145
|
+
{ binding: 4, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3146
|
+
{ binding: 5, resource: { buffer: paramsBuf } }
|
|
3147
|
+
];
|
|
3148
|
+
workgroupCount = [Math.ceil(d_out / 64), seq_len, 1];
|
|
3149
|
+
} else if (node.opType === "LayerNorm") {
|
|
3150
|
+
wgslCode = ShaderLibrary2.getLayerNormShader();
|
|
3151
|
+
const d_model = node.params.d_model;
|
|
3152
|
+
const seq_len = node.inputs.input.shape.slice(0, -1).reduce((a, b) => a * b, 1);
|
|
3153
|
+
const p = new Uint32Array([seq_len, d_model]);
|
|
3154
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3155
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3156
|
+
bindGroupEntries = [
|
|
3157
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3158
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3159
|
+
{ binding: 2, resource: { buffer: node.inputs.bias.gpuBuffer } },
|
|
3160
|
+
{ binding: 3, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3161
|
+
{ binding: 4, resource: { buffer: paramsBuf } }
|
|
3162
|
+
];
|
|
3163
|
+
workgroupCount = [Math.ceil(seq_len / 64), 1, 1];
|
|
3164
|
+
} else if (node.opType === "GELU") {
|
|
3165
|
+
wgslCode = ShaderLibrary2.getGELUShader();
|
|
3166
|
+
const num_elements = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3167
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3168
|
+
this.device.queue.writeBuffer(paramsBuf, 0, new Uint32Array([num_elements]));
|
|
3169
|
+
bindGroupEntries = [
|
|
3170
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3171
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3172
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3173
|
+
];
|
|
3174
|
+
workgroupCount = [Math.ceil(num_elements / 64), 1, 1];
|
|
3175
|
+
} else if (node.opType === "Embedding") {
|
|
3176
|
+
wgslCode = ShaderLibrary2.getEmbeddingShader();
|
|
3177
|
+
const seq_len = node.inputs.input.shape.reduce((a, b) => a * b, 1);
|
|
3178
|
+
const d_model = node.outputs.out.shape[node.outputs.out.shape.length - 1];
|
|
3179
|
+
const p = new Uint32Array([seq_len, d_model]);
|
|
3180
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3181
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3182
|
+
bindGroupEntries = [
|
|
3183
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3184
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3185
|
+
{ binding: 2, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3186
|
+
{ binding: 3, resource: { buffer: paramsBuf } }
|
|
3187
|
+
];
|
|
3188
|
+
workgroupCount = [Math.ceil(seq_len / 64), 1, 1];
|
|
3189
|
+
} else if (node.opType === "SDPA") {
|
|
3190
|
+
wgslCode = ShaderLibrary2.getSDPAShader();
|
|
3191
|
+
const seq_len = node.inputs.qkv.shape[1];
|
|
3192
|
+
const d_model = node.outputs.out.shape[2];
|
|
3193
|
+
const num_heads = node.params.heads || 8;
|
|
3194
|
+
const head_dim = d_model / num_heads;
|
|
3195
|
+
const scale = node.params.scale !== void 0 ? node.params.scale : 1 / Math.sqrt(head_dim);
|
|
3196
|
+
const p = new ArrayBuffer(20);
|
|
3197
|
+
const p_u32 = new Uint32Array(p);
|
|
3198
|
+
const p_f32 = new Float32Array(p);
|
|
3199
|
+
p_u32[0] = seq_len;
|
|
3200
|
+
p_u32[1] = d_model;
|
|
3201
|
+
p_u32[2] = num_heads;
|
|
3202
|
+
p_u32[3] = head_dim;
|
|
3203
|
+
p_f32[4] = scale;
|
|
3204
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3205
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3206
|
+
bindGroupEntries = [
|
|
3207
|
+
{ binding: 0, resource: { buffer: node.inputs.qkv.gpuBuffer } },
|
|
3208
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3209
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3210
|
+
];
|
|
3211
|
+
workgroupCount = [Math.ceil(seq_len / 64), num_heads, 1];
|
|
3212
|
+
} else if (node.opType === "CrossSDPA") {
|
|
3213
|
+
wgslCode = ShaderLibrary2.getCrossSDPAShader();
|
|
3214
|
+
const seq_len_q = node.inputs.q.shape[1];
|
|
3215
|
+
const seq_len_kv = node.inputs.k.shape[1];
|
|
3216
|
+
const d_model = node.inputs.q.shape[2];
|
|
3217
|
+
const num_heads = node.params.heads || 8;
|
|
3218
|
+
const head_dim = d_model / num_heads;
|
|
3219
|
+
const scale = 1 / Math.sqrt(head_dim);
|
|
3220
|
+
const p = new ArrayBuffer(24);
|
|
3221
|
+
const p_u32 = new Uint32Array(p);
|
|
3222
|
+
const p_f32 = new Float32Array(p);
|
|
3223
|
+
p_u32[0] = seq_len_q;
|
|
3224
|
+
p_u32[1] = seq_len_kv;
|
|
3225
|
+
p_u32[2] = d_model;
|
|
3226
|
+
p_u32[3] = num_heads;
|
|
3227
|
+
p_u32[4] = head_dim;
|
|
3228
|
+
p_f32[5] = scale;
|
|
3229
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3230
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3231
|
+
bindGroupEntries = [
|
|
3232
|
+
{ binding: 0, resource: { buffer: node.inputs.q.gpuBuffer } },
|
|
3233
|
+
{ binding: 1, resource: { buffer: node.inputs.k.gpuBuffer } },
|
|
3234
|
+
{ binding: 2, resource: { buffer: node.inputs.v.gpuBuffer } },
|
|
3235
|
+
{ binding: 3, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3236
|
+
{ binding: 4, resource: { buffer: paramsBuf } }
|
|
3237
|
+
];
|
|
3238
|
+
workgroupCount = [Math.ceil(seq_len_q / 64), num_heads, 1];
|
|
3239
|
+
} else if (node.opType === "CrossAttention") {
|
|
3240
|
+
wgslCode = ShaderLibrary2.getCrossAttentionShader();
|
|
3241
|
+
const seq_len_q = node.inputs.q.shape[1];
|
|
3242
|
+
const seq_len_kv = node.inputs.kv.shape[1];
|
|
3243
|
+
const d_model = node.outputs.out.shape[2];
|
|
3244
|
+
const num_heads = node.params.heads || 8;
|
|
3245
|
+
const head_dim = d_model / num_heads;
|
|
3246
|
+
const scale_factor = 1 / Math.sqrt(head_dim);
|
|
3247
|
+
const p = new ArrayBuffer(32);
|
|
3248
|
+
const p_u32 = new Uint32Array(p);
|
|
3249
|
+
const p_f32 = new Float32Array(p);
|
|
3250
|
+
p_u32[0] = seq_len_q;
|
|
3251
|
+
p_u32[1] = seq_len_kv;
|
|
3252
|
+
p_u32[2] = d_model;
|
|
3253
|
+
p_u32[3] = num_heads;
|
|
3254
|
+
p_u32[4] = head_dim;
|
|
3255
|
+
p_f32[5] = scale_factor;
|
|
3256
|
+
p_u32[6] = node.inputs.scale ? 1 : 0;
|
|
3257
|
+
p_u32[7] = node.inputs.bias ? 1 : 0;
|
|
3258
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3259
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3260
|
+
const dummyScale = this.device.createBuffer({ size: 4096, usage: GPUBufferUsage.STORAGE });
|
|
3261
|
+
const dummyBias = this.device.createBuffer({ size: 4096, usage: GPUBufferUsage.STORAGE });
|
|
3262
|
+
bindGroupEntries = [
|
|
3263
|
+
{ binding: 0, resource: { buffer: node.inputs.q.gpuBuffer } },
|
|
3264
|
+
{ binding: 1, resource: { buffer: node.inputs.kv.gpuBuffer } },
|
|
3265
|
+
{ binding: 2, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3266
|
+
{ binding: 3, resource: { buffer: node.inputs.scale ? node.inputs.scale.gpuBuffer : dummyScale } },
|
|
3267
|
+
{ binding: 4, resource: { buffer: node.inputs.bias ? node.inputs.bias.gpuBuffer : dummyBias } },
|
|
3268
|
+
{ binding: 5, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3269
|
+
{ binding: 6, resource: { buffer: paramsBuf } }
|
|
3270
|
+
];
|
|
3271
|
+
workgroupCount = [Math.ceil(seq_len_q / 64), num_heads, 1];
|
|
3272
|
+
} else if (node.opType === "MeanHeight") {
|
|
3273
|
+
wgslCode = ShaderLibrary2.getMeanHeightShader();
|
|
3274
|
+
const p = new Uint32Array([node.inputs.input.shape[1], node.inputs.input.shape[2], node.inputs.input.shape[3]]);
|
|
3275
|
+
const paramBuf = this.device.createBuffer({ size: Math.ceil(p.byteLength / 16) * 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3276
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3277
|
+
bindGroupEntries = [
|
|
3278
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3279
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3280
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3281
|
+
];
|
|
3282
|
+
workgroupCount = [Math.ceil(node.inputs.input.shape[2] / 64), node.inputs.input.shape[3], 1];
|
|
3283
|
+
} else if (node.opType === "ReLU" || node.opType === "Sigmoid" || node.opType === "HardSwish" || node.opType === "HardSigmoid" || node.opType === "SiLU" || node.opType === "Swish" || node.opType === "Tanh" || node.opType === "Reshape" || node.opType === "Squeeze" || node.opType === "Unsqueeze" || node.opType === "Flatten" || node.opType === "Dropout" || node.opType === "Identity") {
|
|
3284
|
+
if (node.opType === "ReLU") wgslCode = ShaderLibrary2.getReLUShader();
|
|
3285
|
+
else if (node.opType === "Sigmoid") wgslCode = ShaderLibrary2.getSigmoidShader();
|
|
3286
|
+
else if (node.opType === "HardSwish") wgslCode = ShaderLibrary2.getHardSwishShader();
|
|
3287
|
+
else if (node.opType === "HardSigmoid") wgslCode = ShaderLibrary2.getHardSigmoidShader();
|
|
3288
|
+
else if (node.opType === "SiLU" || node.opType === "Swish") wgslCode = ShaderLibrary2.getSiLUShader();
|
|
3289
|
+
else if (node.opType === "Tanh") wgslCode = ShaderLibrary2.getTanhShader();
|
|
3290
|
+
else wgslCode = ShaderLibrary2.getCopyShader();
|
|
3291
|
+
const elements = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3292
|
+
const p = new Uint32Array([elements]);
|
|
3293
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3294
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3295
|
+
bindGroupEntries = [
|
|
3296
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3297
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3298
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3299
|
+
];
|
|
3300
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3301
|
+
} else if (node.opType === "Add" || node.opType === "Mul" || node.opType === "Sub" || node.opType === "Div") {
|
|
3302
|
+
const binOp = {
|
|
3303
|
+
Add: "out_val = av + bv;",
|
|
3304
|
+
Mul: "out_val = av * bv;",
|
|
3305
|
+
Sub: "out_val = av - bv;",
|
|
3306
|
+
Div: "out_val = av / bv;"
|
|
3307
|
+
}[node.opType];
|
|
3308
|
+
wgslCode = ShaderLibrary2.getBroadcastBinaryShader(binOp);
|
|
3309
|
+
const outShape = node.outputs.out.shape;
|
|
3310
|
+
const rank = outShape.length;
|
|
3311
|
+
const contigStrides = (shape) => {
|
|
3312
|
+
const st = new Array(shape.length);
|
|
3313
|
+
let s = 1;
|
|
3314
|
+
for (let i = shape.length - 1; i >= 0; i--) {
|
|
3315
|
+
st[i] = s;
|
|
3316
|
+
s *= shape[i];
|
|
3317
|
+
}
|
|
3318
|
+
return st;
|
|
3319
|
+
};
|
|
3320
|
+
const outStrides = contigStrides(outShape);
|
|
3321
|
+
const bcastStrides = (shape) => {
|
|
3322
|
+
const padded = new Array(rank).fill(1);
|
|
3323
|
+
for (let i = 0; i < shape.length; i++) padded[rank - shape.length + i] = shape[i];
|
|
3324
|
+
const st = contigStrides(padded);
|
|
3325
|
+
for (let i = 0; i < rank; i++) if (padded[i] === 1 && outShape[i] !== 1) st[i] = 0;
|
|
3326
|
+
return st;
|
|
3327
|
+
};
|
|
3328
|
+
const aStrides = bcastStrides(node.inputs.a.shape);
|
|
3329
|
+
const bStrides = bcastStrides(node.inputs.b.shape);
|
|
3330
|
+
const total = outShape.reduce((a, b) => a * b, 1);
|
|
3331
|
+
const meta = new Uint32Array(2 + rank * 3);
|
|
3332
|
+
meta[0] = total;
|
|
3333
|
+
meta[1] = rank;
|
|
3334
|
+
for (let d = 0; d < rank; d++) {
|
|
3335
|
+
meta[2 + d] = outStrides[d];
|
|
3336
|
+
meta[2 + rank + d] = aStrides[d];
|
|
3337
|
+
meta[2 + 2 * rank + d] = bStrides[d];
|
|
3338
|
+
}
|
|
3339
|
+
const metaBuf = this.device.createBuffer({ size: Math.ceil(meta.byteLength / 4) * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
|
3340
|
+
this.device.queue.writeBuffer(metaBuf, 0, meta);
|
|
3341
|
+
bindGroupEntries = [
|
|
3342
|
+
{ binding: 0, resource: { buffer: node.inputs.a.gpuBuffer } },
|
|
3343
|
+
{ binding: 1, resource: { buffer: node.inputs.b.gpuBuffer } },
|
|
3344
|
+
{ binding: 2, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3345
|
+
{ binding: 3, resource: { buffer: metaBuf } }
|
|
3346
|
+
];
|
|
3347
|
+
workgroupCount = [Math.ceil(total / 64), 1, 1];
|
|
3348
|
+
} else if (node.opType === "Transpose") {
|
|
3349
|
+
wgslCode = ShaderLibrary2.getGeneralTransposeShader();
|
|
3350
|
+
const inShape = node.inputs.input.shape;
|
|
3351
|
+
const rank = inShape.length;
|
|
3352
|
+
const perm = node.params.perm || [...Array(rank).keys()].reverse();
|
|
3353
|
+
const inStrides = new Array(rank);
|
|
3354
|
+
{
|
|
3355
|
+
let s = 1;
|
|
3356
|
+
for (let i = rank - 1; i >= 0; i--) {
|
|
3357
|
+
inStrides[i] = s;
|
|
3358
|
+
s *= inShape[i];
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const outShape = perm.map((pp) => inShape[pp]);
|
|
3362
|
+
const outStrides = new Array(rank);
|
|
3363
|
+
{
|
|
3364
|
+
let s = 1;
|
|
3365
|
+
for (let i = rank - 1; i >= 0; i--) {
|
|
3366
|
+
outStrides[i] = s;
|
|
3367
|
+
s *= outShape[i];
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
const total = inShape.reduce((a, b) => a * b, 1);
|
|
3371
|
+
const meta = new Uint32Array(2 + rank * 2);
|
|
3372
|
+
meta[0] = total;
|
|
3373
|
+
meta[1] = rank;
|
|
3374
|
+
for (let d = 0; d < rank; d++) {
|
|
3375
|
+
meta[2 + d] = outStrides[d];
|
|
3376
|
+
meta[2 + rank + d] = inStrides[perm[d]];
|
|
3377
|
+
}
|
|
3378
|
+
const metaBuf = this.device.createBuffer({ size: Math.ceil(meta.byteLength / 4) * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
|
3379
|
+
this.device.queue.writeBuffer(metaBuf, 0, meta);
|
|
3380
|
+
bindGroupEntries = [
|
|
3381
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3382
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3383
|
+
{ binding: 2, resource: { buffer: metaBuf } }
|
|
3384
|
+
];
|
|
3385
|
+
workgroupCount = [Math.ceil(total / 64), 1, 1];
|
|
3386
|
+
} else if (node.opType === "Softmax" || node.opType === "LogSoftmax") {
|
|
3387
|
+
wgslCode = node.opType === "Softmax" ? ShaderLibrary2.getSoftmaxShader() : ShaderLibrary2.getLogSoftmaxShader();
|
|
3388
|
+
const shape = node.inputs.input.shape;
|
|
3389
|
+
const d = shape[shape.length - 1];
|
|
3390
|
+
const b = shape.reduce((a, x) => a * x, 1) / d;
|
|
3391
|
+
if (node.params.axis !== void 0) {
|
|
3392
|
+
let ax = node.params.axis;
|
|
3393
|
+
if (ax < 0) ax += shape.length;
|
|
3394
|
+
if (ax !== shape.length - 1) console.warn(`[VolvoxAI WebGPU] ${node.opType} axis ${node.params.axis} != last; using last-axis.`);
|
|
3395
|
+
}
|
|
3396
|
+
const p = new Uint32Array([b, d]);
|
|
3397
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3398
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3399
|
+
bindGroupEntries = [
|
|
3400
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3401
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3402
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3403
|
+
];
|
|
3404
|
+
workgroupCount = [Math.ceil(b / 64), 1, 1];
|
|
3405
|
+
} else if (node.opType === "LeakyReLU") {
|
|
3406
|
+
wgslCode = ShaderLibrary2.getLeakyReLUShader();
|
|
3407
|
+
const elements = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3408
|
+
const alpha = node.params.alpha !== void 0 ? node.params.alpha : 0.01;
|
|
3409
|
+
const p = new ArrayBuffer(16);
|
|
3410
|
+
new Uint32Array(p, 0, 1)[0] = elements;
|
|
3411
|
+
new Float32Array(p, 4, 1)[0] = alpha;
|
|
3412
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3413
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3414
|
+
bindGroupEntries = [
|
|
3415
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3416
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3417
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3418
|
+
];
|
|
3419
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3420
|
+
} else if (node.opType === "PReLU") {
|
|
3421
|
+
wgslCode = ShaderLibrary2.getPReLUShader();
|
|
3422
|
+
const shape = node.inputs.input.shape;
|
|
3423
|
+
const c = shape[shape.length - 1] || 1;
|
|
3424
|
+
const elements = shape.reduce((a, b) => a * b, 1);
|
|
3425
|
+
const p = new Uint32Array([elements, c]);
|
|
3426
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3427
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3428
|
+
bindGroupEntries = [
|
|
3429
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3430
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3431
|
+
{ binding: 2, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3432
|
+
{ binding: 3, resource: { buffer: paramsBuf } }
|
|
3433
|
+
];
|
|
3434
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3435
|
+
} else if (node.opType === "RMSNorm") {
|
|
3436
|
+
wgslCode = ShaderLibrary2.getRMSNormShader();
|
|
3437
|
+
const shape = node.inputs.input.shape;
|
|
3438
|
+
const d_model = node.params.d_model || shape[shape.length - 1];
|
|
3439
|
+
const seq_len = shape.reduce((a, x) => a * x, 1) / d_model;
|
|
3440
|
+
const eps = node.params.eps !== void 0 ? node.params.eps : 1e-6;
|
|
3441
|
+
const p = new ArrayBuffer(16);
|
|
3442
|
+
new Uint32Array(p, 0, 2).set([seq_len, d_model]);
|
|
3443
|
+
new Float32Array(p, 8, 1)[0] = eps;
|
|
3444
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3445
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3446
|
+
bindGroupEntries = [
|
|
3447
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3448
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3449
|
+
{ binding: 2, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3450
|
+
{ binding: 3, resource: { buffer: paramsBuf } }
|
|
3451
|
+
];
|
|
3452
|
+
workgroupCount = [Math.ceil(seq_len / 64), 1, 1];
|
|
3453
|
+
} else if (node.opType === "GlobalAveragePool") {
|
|
3454
|
+
wgslCode = ShaderLibrary2.getGlobalAveragePoolShader();
|
|
3455
|
+
const inShape = node.inputs.input.shape;
|
|
3456
|
+
const B = inShape[0];
|
|
3457
|
+
const H = inShape[1] || 1;
|
|
3458
|
+
const W = inShape[2] || 1;
|
|
3459
|
+
const C = inShape[3] || 1;
|
|
3460
|
+
const p = new Uint32Array([B, H, W, C]);
|
|
3461
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3462
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3463
|
+
bindGroupEntries = [
|
|
3464
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3465
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3466
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3467
|
+
];
|
|
3468
|
+
workgroupCount = [Math.ceil(C / 64), B, 1];
|
|
3469
|
+
} else if (node.opType === "BatchNorm2D") {
|
|
3470
|
+
wgslCode = ShaderLibrary2.getBatchNorm2DShader();
|
|
3471
|
+
const [bn, hn, wn, cn] = node.inputs.input.shape;
|
|
3472
|
+
const eps = node.params.eps !== void 0 ? node.params.eps : 1e-5;
|
|
3473
|
+
const p = new ArrayBuffer(32);
|
|
3474
|
+
new Uint32Array(p, 0, 4).set([bn, cn, hn, wn]);
|
|
3475
|
+
new Float32Array(p, 16, 1)[0] = eps;
|
|
3476
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3477
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3478
|
+
bindGroupEntries = [
|
|
3479
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3480
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3481
|
+
{ binding: 2, resource: { buffer: node.inputs.bias.gpuBuffer } },
|
|
3482
|
+
{ binding: 3, resource: { buffer: node.inputs.running_mean.gpuBuffer } },
|
|
3483
|
+
{ binding: 4, resource: { buffer: node.inputs.running_var.gpuBuffer } },
|
|
3484
|
+
{ binding: 5, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3485
|
+
{ binding: 6, resource: { buffer: paramsBuf } }
|
|
3486
|
+
];
|
|
3487
|
+
workgroupCount = [Math.ceil(bn * cn * hn * wn / 64), 1, 1];
|
|
3488
|
+
} else if (node.opType === "Resize" || node.opType === "ResizeNearest2D") {
|
|
3489
|
+
wgslCode = ShaderLibrary2.getResizeShader();
|
|
3490
|
+
const [rb, inH, inW, rc] = node.inputs.input.shape;
|
|
3491
|
+
const [, outH, outW] = node.outputs.out.shape;
|
|
3492
|
+
const mode = node.opType === "ResizeNearest2D" || node.params.mode === "nearest" ? 0 : 1;
|
|
3493
|
+
const p = new Uint32Array([rb, inH, inW, rc, outH, outW, mode, 0]);
|
|
3494
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3495
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3496
|
+
bindGroupEntries = [
|
|
3497
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3498
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3499
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3500
|
+
];
|
|
3501
|
+
workgroupCount = [Math.ceil(outW / 8), Math.ceil(outH / 8), rb * rc];
|
|
3502
|
+
} else if (node.opType === "Split") {
|
|
3503
|
+
const inShape = node.inputs.input.shape;
|
|
3504
|
+
let axis = node.params.axis || 0;
|
|
3505
|
+
if (axis < 0) axis += inShape.length;
|
|
3506
|
+
const outKeys = Object.keys(node.outputs).sort();
|
|
3507
|
+
const numOutputs = outKeys.length;
|
|
3508
|
+
const splitSize = inShape[axis] / numOutputs;
|
|
3509
|
+
let inner = 1;
|
|
3510
|
+
for (let i = axis + 1; i < inShape.length; i++) inner *= inShape[i];
|
|
3511
|
+
const shaderModule2 = this.device.createShaderModule({ code: ShaderLibrary2.getSplitShader() });
|
|
3512
|
+
const pipeline2 = await this.device.createComputePipelineAsync({
|
|
3513
|
+
layout: "auto",
|
|
3514
|
+
compute: { module: shaderModule2, entryPoint: "main" }
|
|
3515
|
+
});
|
|
3516
|
+
for (let o = 0; o < numOutputs; o++) {
|
|
3517
|
+
const outT = node.outputs[outKeys[o]];
|
|
3518
|
+
const total = outT.shape.reduce((a, b) => a * b, 1);
|
|
3519
|
+
const p = new Uint32Array([total, inner, splitSize, inShape[axis], o * splitSize]);
|
|
3520
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3521
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3522
|
+
const bindGroup2 = this.device.createBindGroup({
|
|
3523
|
+
layout: pipeline2.getBindGroupLayout(0),
|
|
3524
|
+
entries: [
|
|
3525
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3526
|
+
{ binding: 1, resource: { buffer: outT.gpuBuffer } },
|
|
3527
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3528
|
+
]
|
|
3529
|
+
});
|
|
3530
|
+
this.pipelines.push({
|
|
3531
|
+
pipeline: pipeline2,
|
|
3532
|
+
bindGroup: bindGroup2,
|
|
3533
|
+
workgroupCount: [Math.ceil(total / 64), 1, 1],
|
|
3534
|
+
nodeName: `${node.id}_split${o}`
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
return;
|
|
3538
|
+
} else if (node.opType === "Clip") {
|
|
3539
|
+
wgslCode = ShaderLibrary2.getClipShader();
|
|
3540
|
+
const elements = node.inputs.input.sizeBytes / 4;
|
|
3541
|
+
let minVal = node.params.min !== void 0 ? node.params.min : -1e9;
|
|
3542
|
+
let maxVal = node.params.max !== void 0 ? node.params.max : 1e9;
|
|
3543
|
+
if (node.inputs.min) minVal = new Float32Array(node.inputs.min.buffer)[0];
|
|
3544
|
+
if (node.inputs.max) maxVal = new Float32Array(node.inputs.max.buffer)[0];
|
|
3545
|
+
const p = new Float32Array([0, minVal, maxVal, 0]);
|
|
3546
|
+
new Uint32Array(p.buffer)[0] = elements;
|
|
3547
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3548
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3549
|
+
bindGroupEntries = [
|
|
3550
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3551
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3552
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3553
|
+
];
|
|
3554
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3555
|
+
} else if (node.opType === "MaxPool2D") {
|
|
3556
|
+
wgslCode = ShaderLibrary2.getMaxPool2DShader();
|
|
3557
|
+
const [ky, kx] = _pair(node.params.kernel, 1);
|
|
3558
|
+
const [sy, sx] = _pair(node.params.stride, 1);
|
|
3559
|
+
const py = node.params.padding ? node.params.padding[0] : 0;
|
|
3560
|
+
const px = node.params.padding ? node.params.padding[1] : 0;
|
|
3561
|
+
const p = new Uint32Array([
|
|
3562
|
+
node.inputs.input.shape[1],
|
|
3563
|
+
node.inputs.input.shape[2],
|
|
3564
|
+
node.inputs.input.shape[3],
|
|
3565
|
+
node.outputs.out.shape[1],
|
|
3566
|
+
node.outputs.out.shape[2],
|
|
3567
|
+
ky,
|
|
3568
|
+
kx,
|
|
3569
|
+
sy,
|
|
3570
|
+
sx,
|
|
3571
|
+
py,
|
|
3572
|
+
px
|
|
3573
|
+
]);
|
|
3574
|
+
const paramBuf = this.device.createBuffer({ size: Math.ceil(p.byteLength / 16) * 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3575
|
+
this.device.queue.writeBuffer(paramBuf, 0, p);
|
|
3576
|
+
bindGroupEntries = [
|
|
3577
|
+
{ binding: 0, resource: { buffer: node.inputs.input.gpuBuffer } },
|
|
3578
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3579
|
+
{ binding: 2, resource: { buffer: paramBuf } }
|
|
3580
|
+
];
|
|
3581
|
+
workgroupCount = [
|
|
3582
|
+
Math.ceil(node.outputs.out.shape[2] / 8),
|
|
3583
|
+
Math.ceil(node.outputs.out.shape[1] / 8),
|
|
3584
|
+
node.outputs.out.shape[3]
|
|
3585
|
+
];
|
|
3586
|
+
} else if (node.opType === "Cast") {
|
|
3587
|
+
wgslCode = ShaderLibrary2.getCopyShader();
|
|
3588
|
+
const inp = node.inputs.input || node.inputs.data;
|
|
3589
|
+
const elements = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3590
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3591
|
+
this.device.queue.writeBuffer(paramsBuf, 0, new Uint32Array([elements]));
|
|
3592
|
+
bindGroupEntries = [
|
|
3593
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3594
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3595
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3596
|
+
];
|
|
3597
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3598
|
+
} else if (node.opType === "Where" || node.opType === "Mask") {
|
|
3599
|
+
wgslCode = ShaderLibrary2.getWhereShader();
|
|
3600
|
+
const cond = node.inputs.cond || node.inputs.condition;
|
|
3601
|
+
const a = node.inputs.x || node.inputs.a;
|
|
3602
|
+
const b = node.inputs.y || node.inputs.b;
|
|
3603
|
+
const elements = node.outputs.out.shape.reduce((x, y) => x * y, 1);
|
|
3604
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3605
|
+
this.device.queue.writeBuffer(paramsBuf, 0, new Uint32Array([elements]));
|
|
3606
|
+
bindGroupEntries = [
|
|
3607
|
+
{ binding: 0, resource: { buffer: cond.gpuBuffer } },
|
|
3608
|
+
{ binding: 1, resource: { buffer: a.gpuBuffer } },
|
|
3609
|
+
{ binding: 2, resource: { buffer: b.gpuBuffer } },
|
|
3610
|
+
{ binding: 3, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3611
|
+
{ binding: 4, resource: { buffer: paramsBuf } }
|
|
3612
|
+
];
|
|
3613
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3614
|
+
} else if (node.opType === "DequantizeLinear") {
|
|
3615
|
+
wgslCode = ShaderLibrary2.getDequantizeLinearShader();
|
|
3616
|
+
const inp = node.inputs.input || node.inputs.x;
|
|
3617
|
+
const elements = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3618
|
+
const hasZp = node.inputs.zero_point ? 1 : 0;
|
|
3619
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3620
|
+
this.device.queue.writeBuffer(paramsBuf, 0, new Uint32Array([elements, hasZp]));
|
|
3621
|
+
const dummy = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.STORAGE });
|
|
3622
|
+
bindGroupEntries = [
|
|
3623
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3624
|
+
{ binding: 1, resource: { buffer: node.inputs.scale.gpuBuffer } },
|
|
3625
|
+
{ binding: 2, resource: { buffer: hasZp ? node.inputs.zero_point.gpuBuffer : dummy } },
|
|
3626
|
+
{ binding: 3, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3627
|
+
{ binding: 4, resource: { buffer: paramsBuf } }
|
|
3628
|
+
];
|
|
3629
|
+
workgroupCount = [Math.ceil(elements / 64), 1, 1];
|
|
3630
|
+
} else if (node.opType === "Expand" || node.opType === "Broadcast") {
|
|
3631
|
+
wgslCode = ShaderLibrary2.getExpandShader();
|
|
3632
|
+
const inp = node.inputs.input || node.inputs.data;
|
|
3633
|
+
const pad4 = (sh) => [1, 1, 1, 1].slice(0, 4 - sh.length).concat(sh);
|
|
3634
|
+
const [ib, ih, iw, ic] = pad4(inp.shape);
|
|
3635
|
+
const [ob, oh, ow, oc] = pad4(node.outputs.out.shape);
|
|
3636
|
+
const p = new Uint32Array([ib, ih, iw, ic, ob, oh, ow, oc]);
|
|
3637
|
+
const paramsBuf = this.device.createBuffer({ size: 32, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3638
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3639
|
+
bindGroupEntries = [
|
|
3640
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3641
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3642
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3643
|
+
];
|
|
3644
|
+
workgroupCount = [Math.ceil(ob * oh * ow * oc / 64), 1, 1];
|
|
3645
|
+
} else if (node.opType === "Pad") {
|
|
3646
|
+
wgslCode = ShaderLibrary2.getPadShader();
|
|
3647
|
+
const inp = node.inputs.input || node.inputs.data;
|
|
3648
|
+
const pads = node.params.pads || [];
|
|
3649
|
+
const pt = pads.length === 8 ? pads[1] : pads[0] || 0;
|
|
3650
|
+
const pl = pads.length === 8 ? pads[2] : pads[1] || 0;
|
|
3651
|
+
const val = node.params.value || 0;
|
|
3652
|
+
const is = [1, 1, 1, 1].slice(0, 4 - inp.shape.length).concat(inp.shape);
|
|
3653
|
+
const os = [1, 1, 1, 1].slice(0, 4 - node.outputs.out.shape.length).concat(node.outputs.out.shape);
|
|
3654
|
+
const buf = new ArrayBuffer(48);
|
|
3655
|
+
const u = new Uint32Array(buf);
|
|
3656
|
+
const f = new Float32Array(buf);
|
|
3657
|
+
u[0] = is[0];
|
|
3658
|
+
u[1] = is[1];
|
|
3659
|
+
u[2] = is[2];
|
|
3660
|
+
u[3] = is[3];
|
|
3661
|
+
u[4] = os[1];
|
|
3662
|
+
u[5] = os[2];
|
|
3663
|
+
u[6] = pt;
|
|
3664
|
+
u[7] = pl;
|
|
3665
|
+
f[8] = val;
|
|
3666
|
+
const paramsBuf = this.device.createBuffer({ size: 48, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3667
|
+
this.device.queue.writeBuffer(paramsBuf, 0, buf);
|
|
3668
|
+
bindGroupEntries = [
|
|
3669
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3670
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3671
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3672
|
+
];
|
|
3673
|
+
workgroupCount = [Math.ceil(os[0] * os[1] * os[2] * os[3] / 64), 1, 1];
|
|
3674
|
+
} else if (node.opType === "Slice") {
|
|
3675
|
+
wgslCode = ShaderLibrary2.getSliceShader();
|
|
3676
|
+
const inp = node.inputs.input || node.inputs.data;
|
|
3677
|
+
const starts = node.params.starts || [0, 0, 0, 0];
|
|
3678
|
+
const steps = node.params.steps || [1, 1, 1, 1];
|
|
3679
|
+
const axes = node.params.axes || [0, 1, 2, 3];
|
|
3680
|
+
const in_s = [1, 1, 1, 1].slice(0, 4 - inp.shape.length).concat(inp.shape);
|
|
3681
|
+
const out_s = [1, 1, 1, 1].slice(0, 4 - node.outputs.out.shape.length).concat(node.outputs.out.shape);
|
|
3682
|
+
const st = [0, 0, 0, 0], sp = [1, 1, 1, 1];
|
|
3683
|
+
for (let i = 0; i < axes.length; i++) {
|
|
3684
|
+
let ax = axes[i];
|
|
3685
|
+
if (ax < 0) ax += inp.shape.length;
|
|
3686
|
+
ax += 4 - inp.shape.length;
|
|
3687
|
+
st[ax] = starts[i] < 0 ? starts[i] + in_s[ax] : starts[i];
|
|
3688
|
+
sp[ax] = steps[i];
|
|
3689
|
+
}
|
|
3690
|
+
const total = out_s[0] * out_s[1] * out_s[2] * out_s[3];
|
|
3691
|
+
const p = new Uint32Array([
|
|
3692
|
+
out_s[0],
|
|
3693
|
+
out_s[1],
|
|
3694
|
+
out_s[2],
|
|
3695
|
+
out_s[3],
|
|
3696
|
+
in_s[1],
|
|
3697
|
+
in_s[2],
|
|
3698
|
+
in_s[3],
|
|
3699
|
+
st[0],
|
|
3700
|
+
st[1],
|
|
3701
|
+
st[2],
|
|
3702
|
+
st[3],
|
|
3703
|
+
sp[0],
|
|
3704
|
+
sp[1],
|
|
3705
|
+
sp[2],
|
|
3706
|
+
sp[3],
|
|
3707
|
+
total
|
|
3708
|
+
]);
|
|
3709
|
+
const paramsBuf = this.device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3710
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3711
|
+
bindGroupEntries = [
|
|
3712
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3713
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3714
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3715
|
+
];
|
|
3716
|
+
workgroupCount = [Math.ceil(total / 64), 1, 1];
|
|
3717
|
+
} else if (node.opType === "Gather") {
|
|
3718
|
+
let axis = node.params.axis || 0;
|
|
3719
|
+
if (axis < 0) axis += node.inputs.input.shape.length;
|
|
3720
|
+
if (axis !== 0) {
|
|
3721
|
+
console.warn(`[VolvoxAI WebGPU] Gather axis ${axis} not supported on GPU; node ${node.id} skipped (use WASM/CPU).`);
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
wgslCode = ShaderLibrary2.getGatherShader();
|
|
3725
|
+
const inp = node.inputs.input;
|
|
3726
|
+
const rowSize = inp.shape.slice(1).reduce((a, b) => a * b, 1) || 1;
|
|
3727
|
+
const total = node.outputs.out.shape.reduce((a, b) => a * b, 1);
|
|
3728
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3729
|
+
this.device.queue.writeBuffer(paramsBuf, 0, new Uint32Array([rowSize, total / rowSize, total]));
|
|
3730
|
+
bindGroupEntries = [
|
|
3731
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3732
|
+
{ binding: 1, resource: { buffer: node.inputs.indices.gpuBuffer } },
|
|
3733
|
+
{ binding: 2, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3734
|
+
{ binding: 3, resource: { buffer: paramsBuf } }
|
|
3735
|
+
];
|
|
3736
|
+
workgroupCount = [Math.ceil(total / 64), 1, 1];
|
|
3737
|
+
} else if (node.opType === "ReduceSum" || node.opType === "ReduceMean") {
|
|
3738
|
+
wgslCode = ShaderLibrary2.getReduceShader();
|
|
3739
|
+
const inp = node.inputs.input || node.inputs.data;
|
|
3740
|
+
const in_shape = inp.shape.length === 2 ? inp.shape : [1, inp.shape.reduce((a, b2) => a * b2, 1)];
|
|
3741
|
+
const b = in_shape[0], d = in_shape[1];
|
|
3742
|
+
const inv = node.opType === "ReduceMean" ? 1 / d : 1;
|
|
3743
|
+
const buf = new ArrayBuffer(16);
|
|
3744
|
+
new Uint32Array(buf, 0, 2).set([b, d]);
|
|
3745
|
+
new Float32Array(buf, 8, 1)[0] = inv;
|
|
3746
|
+
const paramsBuf = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3747
|
+
this.device.queue.writeBuffer(paramsBuf, 0, buf);
|
|
3748
|
+
bindGroupEntries = [
|
|
3749
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3750
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3751
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3752
|
+
];
|
|
3753
|
+
workgroupCount = [Math.ceil(b / 64), 1, 1];
|
|
3754
|
+
} else if (node.opType === "AveragePool" || node.opType === "AveragePool2D") {
|
|
3755
|
+
wgslCode = ShaderLibrary2.getAveragePool2DShader();
|
|
3756
|
+
const inp = node.inputs.input || node.inputs.x;
|
|
3757
|
+
const [b, in_h, in_w, c] = inp.shape;
|
|
3758
|
+
const [, out_h, out_w] = node.outputs.out.shape;
|
|
3759
|
+
const [kh, kw] = _pair(node.params.kernel, 1);
|
|
3760
|
+
const [sh, sw] = _pair(node.params.stride, 1);
|
|
3761
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
3762
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
3763
|
+
const p = new Uint32Array([b, in_h, in_w, c, out_h, out_w, kh, kw, sh, sw, ph, pw]);
|
|
3764
|
+
const paramsBuf = this.device.createBuffer({ size: 48, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3765
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3766
|
+
bindGroupEntries = [
|
|
3767
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3768
|
+
{ binding: 1, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3769
|
+
{ binding: 2, resource: { buffer: paramsBuf } }
|
|
3770
|
+
];
|
|
3771
|
+
workgroupCount = [Math.ceil(out_w / 8), Math.ceil(out_h / 8), b * c];
|
|
3772
|
+
} else if (node.opType === "ConvTranspose2D") {
|
|
3773
|
+
wgslCode = ShaderLibrary2.getConvTranspose2DShader();
|
|
3774
|
+
const inp = node.inputs.input || node.inputs.x;
|
|
3775
|
+
const [b, in_h, in_w, in_c] = inp.shape;
|
|
3776
|
+
const [, out_h, out_w, out_c] = node.outputs.out.shape;
|
|
3777
|
+
const kh = node.params.kernel[0], kw = node.params.kernel[1];
|
|
3778
|
+
const sh = node.params.stride ? node.params.stride[0] : 1;
|
|
3779
|
+
const sw = node.params.stride ? node.params.stride[1] : 1;
|
|
3780
|
+
const ph = node.params.padding ? node.params.padding[0] : 0;
|
|
3781
|
+
const pw = node.params.padding ? node.params.padding[1] : 0;
|
|
3782
|
+
const hasBias = node.inputs.bias ? 1 : 0;
|
|
3783
|
+
const p = new Uint32Array([b, in_h, in_w, in_c, out_h, out_w, out_c, kh, kw, sh, sw, ph, pw, hasBias]);
|
|
3784
|
+
const paramsBuf = this.device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
3785
|
+
this.device.queue.writeBuffer(paramsBuf, 0, p);
|
|
3786
|
+
const dummyBias = this.device.createBuffer({ size: 16, usage: GPUBufferUsage.STORAGE });
|
|
3787
|
+
bindGroupEntries = [
|
|
3788
|
+
{ binding: 0, resource: { buffer: inp.gpuBuffer } },
|
|
3789
|
+
{ binding: 1, resource: { buffer: node.inputs.weight.gpuBuffer } },
|
|
3790
|
+
{ binding: 2, resource: { buffer: hasBias ? node.inputs.bias.gpuBuffer : dummyBias } },
|
|
3791
|
+
{ binding: 3, resource: { buffer: node.outputs.out.gpuBuffer } },
|
|
3792
|
+
{ binding: 4, resource: { buffer: paramsBuf } }
|
|
3793
|
+
];
|
|
3794
|
+
workgroupCount = [Math.ceil(out_w / 8), Math.ceil(out_h / 8), b * out_c];
|
|
3795
|
+
} else {
|
|
3796
|
+
console.warn(`[VolvoxAI WebGPU] Shader for ${node.opType} not implemented yet in Executor.`);
|
|
3797
|
+
return;
|
|
3798
|
+
}
|
|
3799
|
+
if (!wgslCode) {
|
|
3800
|
+
console.warn(`[VolvoxAI WebGPU] ${node.opType} has no native GPU shader; node ${node.id} skipped.`);
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
if (isNaN(workgroupCount[0]) || isNaN(workgroupCount[1]) || isNaN(workgroupCount[2]) || workgroupCount[0] <= 0 || workgroupCount[1] <= 0 || workgroupCount[2] <= 0) {
|
|
3804
|
+
console.error(`Invalid workgroupCount [${workgroupCount}] for node ${node.id} (${node.opType})`);
|
|
3805
|
+
workgroupCount = [1, 1, 1];
|
|
3806
|
+
}
|
|
3807
|
+
let shaderModule = this.device.createShaderModule({ code: wgslCode });
|
|
3808
|
+
let pipeline;
|
|
3809
|
+
try {
|
|
3810
|
+
pipeline = await this.device.createComputePipelineAsync({
|
|
3811
|
+
layout: "auto",
|
|
3812
|
+
compute: { module: shaderModule, entryPoint: "main" }
|
|
3813
|
+
});
|
|
3814
|
+
} catch (err) {
|
|
3815
|
+
if (!fallbackWgslCode || fallbackWgslCode === wgslCode || !fallbackWorkgroupCount) throw err;
|
|
3816
|
+
console.warn(`[VolvoxAI WebGPU] Specialized shader for ${node.id} failed; falling back to generic Conv2D.`, err);
|
|
3817
|
+
wgslCode = fallbackWgslCode;
|
|
3818
|
+
workgroupCount = fallbackWorkgroupCount;
|
|
3819
|
+
shaderModule = this.device.createShaderModule({ code: wgslCode });
|
|
3820
|
+
pipeline = await this.device.createComputePipelineAsync({
|
|
3821
|
+
layout: "auto",
|
|
3822
|
+
compute: { module: shaderModule, entryPoint: "main" }
|
|
3823
|
+
});
|
|
3824
|
+
}
|
|
3825
|
+
const bindGroup = this.device.createBindGroup({
|
|
3826
|
+
layout: pipeline.getBindGroupLayout(0),
|
|
3827
|
+
entries: bindGroupEntries
|
|
3828
|
+
});
|
|
3829
|
+
this.pipelines.push({ pipeline, bindGroup, workgroupCount, nodeName: node.id });
|
|
3830
|
+
}
|
|
3831
|
+
/**
|
|
3832
|
+
* Execute the compiled graph on the GPU.
|
|
3833
|
+
* @param {Object} inputs - Key-value pair of input tensor names to Float32Array/Int32Array
|
|
3834
|
+
*/
|
|
3835
|
+
async execute(inputs) {
|
|
3836
|
+
for (const [name, data] of Object.entries(inputs)) {
|
|
3837
|
+
const buffer = this.gpuBuffers.get(name);
|
|
3838
|
+
if (buffer) {
|
|
3839
|
+
this.device.queue.writeBuffer(buffer, 0, data.buffer, data.byteOffset, data.byteLength);
|
|
3840
|
+
}
|
|
3841
|
+
}
|
|
3842
|
+
let commandEncoder = this.device.createCommandEncoder();
|
|
3843
|
+
let passEncoder = commandEncoder.beginComputePass();
|
|
3844
|
+
for (let i = 0; i < this.pipelines.length; i++) {
|
|
3845
|
+
const p = this.pipelines[i];
|
|
3846
|
+
passEncoder.setPipeline(p.pipeline);
|
|
3847
|
+
passEncoder.setBindGroup(0, p.bindGroup);
|
|
3848
|
+
passEncoder.dispatchWorkgroups(p.workgroupCount[0], p.workgroupCount[1], p.workgroupCount[2]);
|
|
3849
|
+
if ((i + 1) % 20 === 0) {
|
|
3850
|
+
passEncoder.end();
|
|
3851
|
+
this.device.queue.submit([commandEncoder.finish()]);
|
|
3852
|
+
commandEncoder = this.device.createCommandEncoder();
|
|
3853
|
+
passEncoder = commandEncoder.beginComputePass();
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
passEncoder.end();
|
|
3857
|
+
this.device.queue.submit([commandEncoder.finish()]);
|
|
3858
|
+
const lastNode = this.graph.nodes[this.graph.nodes.length - 1];
|
|
3859
|
+
const outName = Object.keys(lastNode.outputs)[0];
|
|
3860
|
+
return this.gpuBuffers.get(lastNode.outputs[outName].name);
|
|
3861
|
+
}
|
|
3862
|
+
/**
|
|
3863
|
+
* Helper to read a GPUBuffer back to CPU (Float32Array) for validation.
|
|
3864
|
+
*/
|
|
3865
|
+
async readBuffer(gpuBuffer, sizeBytes) {
|
|
3866
|
+
const stagingBuffer = this.device.createBuffer({
|
|
3867
|
+
size: sizeBytes,
|
|
3868
|
+
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
|
|
3869
|
+
});
|
|
3870
|
+
const commandEncoder = this.device.createCommandEncoder();
|
|
3871
|
+
commandEncoder.copyBufferToBuffer(gpuBuffer, 0, stagingBuffer, 0, sizeBytes);
|
|
3872
|
+
this.device.queue.submit([commandEncoder.finish()]);
|
|
3873
|
+
await stagingBuffer.mapAsync(GPUMapMode.READ);
|
|
3874
|
+
const copyArray = new Float32Array(stagingBuffer.getMappedRange().slice(0));
|
|
3875
|
+
stagingBuffer.unmap();
|
|
3876
|
+
return copyArray;
|
|
3877
|
+
}
|
|
3878
|
+
};
|
|
3879
|
+
|
|
3880
|
+
// js/GraphLoader.js
|
|
3881
|
+
var GraphLoader = class _GraphLoader {
|
|
3882
|
+
/**
|
|
3883
|
+
* Loads a graph from a single Safetensors file.
|
|
3884
|
+
* The Safetensors __metadata__ field must contain a 'volvox_nodes' JSON string.
|
|
3885
|
+
* @param {Object} graphBuilder - An empty Graph instance from VolvoxAI.createGraph()
|
|
3886
|
+
* @param {string} safetensorsUrl - URL to the .safetensors file
|
|
3887
|
+
* @returns {Promise<Graph>} Populated graph
|
|
3888
|
+
*/
|
|
3889
|
+
static async load(graphBuilder, safetensorsUrl) {
|
|
3890
|
+
let configUrl;
|
|
3891
|
+
if (safetensorsUrl.endsWith("_weights.safetensors")) {
|
|
3892
|
+
configUrl = safetensorsUrl.replace("_weights.safetensors", "_config.json");
|
|
3893
|
+
} else {
|
|
3894
|
+
configUrl = safetensorsUrl.replace(/\/[^\/]+$/, "/config.json");
|
|
3895
|
+
}
|
|
3896
|
+
console.log(`[VolvoxAI] Loading config from ${configUrl}...`);
|
|
3897
|
+
const configResponse = await fetch(configUrl);
|
|
3898
|
+
if (!configResponse.ok) throw new Error(`Failed to load config.json: ${configResponse.statusText}`);
|
|
3899
|
+
const config = await configResponse.json();
|
|
3900
|
+
console.log(`[VolvoxAI] Loading Safetensors model from ${safetensorsUrl}...`);
|
|
3901
|
+
const response = await fetch(safetensorsUrl);
|
|
3902
|
+
if (!response.ok) throw new Error(`Failed to load safetensors: ${response.statusText}`);
|
|
3903
|
+
const buffer = await response.arrayBuffer();
|
|
3904
|
+
const dataView = new DataView(buffer);
|
|
3905
|
+
const headerLen = Number(dataView.getBigUint64(0, true));
|
|
3906
|
+
const headerBytes = new Uint8Array(buffer, 8, headerLen);
|
|
3907
|
+
const headerStr = new TextDecoder("utf-8").decode(headerBytes);
|
|
3908
|
+
const header = JSON.parse(headerStr);
|
|
3909
|
+
const metadata = header.__metadata__ || {};
|
|
3910
|
+
const binaryOffset = 8 + headerLen;
|
|
3911
|
+
const tensorsMap = /* @__PURE__ */ new Map();
|
|
3912
|
+
for (const [name, info] of Object.entries(header)) {
|
|
3913
|
+
if (name === "__metadata__") continue;
|
|
3914
|
+
const dtype = info.dtype === "I8" ? "int8" : info.dtype === "U8" ? "uint8" : "float32";
|
|
3915
|
+
const tensor = graphBuilder.addWeight(name, info.shape, dtype);
|
|
3916
|
+
const startByte = binaryOffset + info.data_offsets[0];
|
|
3917
|
+
const lengthBytes = info.data_offsets[1] - info.data_offsets[0];
|
|
3918
|
+
if (dtype === "int8") {
|
|
3919
|
+
tensor.buffer = new Int8Array(buffer, startByte, lengthBytes);
|
|
3920
|
+
} else if (dtype === "uint8") {
|
|
3921
|
+
tensor.buffer = new Uint8Array(buffer, startByte, lengthBytes);
|
|
3922
|
+
} else if (info.dtype === "F16") {
|
|
3923
|
+
tensor.buffer = _GraphLoader._float16ToFloat32Array(buffer, startByte, lengthBytes);
|
|
3924
|
+
} else {
|
|
3925
|
+
tensor.buffer = new Float32Array(buffer, startByte, lengthBytes / 4);
|
|
3926
|
+
}
|
|
3927
|
+
tensorsMap.set(name, tensor);
|
|
3928
|
+
}
|
|
3929
|
+
if (config.inputs) {
|
|
3930
|
+
const inputsDef = config.inputs;
|
|
3931
|
+
for (const [name, info] of Object.entries(inputsDef)) {
|
|
3932
|
+
const tensor = graphBuilder.addInput(name, info.shape, info.dtype || "float32");
|
|
3933
|
+
tensorsMap.set(name, tensor);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
if (config.nodes) {
|
|
3937
|
+
_GraphLoader._buildFromBlueprint(graphBuilder, config, tensorsMap);
|
|
3938
|
+
} else if (config.model_type) {
|
|
3939
|
+
if (typeof _GraphLoader.ModelBuilders === "undefined" || !_GraphLoader.ModelBuilders[config.model_type]) {
|
|
3940
|
+
throw new Error(`[VolvoxAI] Unsupported Hugging Face model_type: '${config.model_type}'. No builder registered.`);
|
|
3941
|
+
}
|
|
3942
|
+
console.log(`[VolvoxAI] Building graph on the fly using model builder for '${config.model_type}'...`);
|
|
3943
|
+
_GraphLoader.ModelBuilders[config.model_type](graphBuilder, config, tensorsMap);
|
|
3944
|
+
} else {
|
|
3945
|
+
throw new Error("[VolvoxAI] config.json must contain either 'nodes' (Volvox blueprint) or 'model_type' (Hugging Face).");
|
|
3946
|
+
}
|
|
3947
|
+
_GraphLoader._dequantizeConvWeights(graphBuilder);
|
|
3948
|
+
_GraphLoader._normalizeConvWeightsForImageLayout(graphBuilder);
|
|
3949
|
+
const usedAsInput = /* @__PURE__ */ new Set();
|
|
3950
|
+
for (const node of graphBuilder.nodes) {
|
|
3951
|
+
for (const t of Object.values(node.inputs)) {
|
|
3952
|
+
if (t && t.name) usedAsInput.add(t.name);
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
const outputNames = [];
|
|
3956
|
+
for (const node of graphBuilder.nodes) {
|
|
3957
|
+
for (const t of Object.values(node.outputs)) {
|
|
3958
|
+
if (t && t.name && !usedAsInput.has(t.name)) {
|
|
3959
|
+
outputNames.push(t.name);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
graphBuilder.outputNames = outputNames;
|
|
3964
|
+
_GraphLoader._resolveMatMulLayouts(graphBuilder);
|
|
3965
|
+
console.log(`[VolvoxAI] Successfully assembled graph. Outputs:`, outputNames);
|
|
3966
|
+
return graphBuilder;
|
|
3967
|
+
}
|
|
3968
|
+
/**
|
|
3969
|
+
* MatMul weights come in two layouts: PyTorch Linear stores [d_out, d_in]
|
|
3970
|
+
* (out = x·Wᵀ); GPT-Neo/Conv1D stores [d_in, d_out] (out = x·W). Non-square weights
|
|
3971
|
+
* disambiguate by shape; square ones can't, so infer the model-wide convention from
|
|
3972
|
+
* the unambiguous weights and tag every MatMul node with `wLayout` ('dout' | 'din').
|
|
3973
|
+
*/
|
|
3974
|
+
static _resolveMatMulLayouts(graph) {
|
|
3975
|
+
const isMM = (n) => n.opType === "MatMul" || n.opType === "Linear" || n.opType === "Gemm";
|
|
3976
|
+
const dims = (n) => {
|
|
3977
|
+
const K = n.inputs.input?.shape?.[n.inputs.input.shape.length - 1];
|
|
3978
|
+
const outT = Object.values(n.outputs)[0];
|
|
3979
|
+
const N = outT?.shape?.[outT.shape.length - 1];
|
|
3980
|
+
return [K, N];
|
|
3981
|
+
};
|
|
3982
|
+
let din = 0, dout = 0;
|
|
3983
|
+
for (const n of graph.nodes) {
|
|
3984
|
+
if (!isMM(n) || !n.inputs.weight || n.inputs.scale) continue;
|
|
3985
|
+
const w = n.inputs.weight.shape;
|
|
3986
|
+
if (!w || w.length < 2) continue;
|
|
3987
|
+
const [K, N] = dims(n);
|
|
3988
|
+
if (K === N) continue;
|
|
3989
|
+
if (w[0] === N && w[1] === K) dout++;
|
|
3990
|
+
else if (w[0] === K && w[1] === N) din++;
|
|
3991
|
+
}
|
|
3992
|
+
const model = din > dout ? "din" : "dout";
|
|
3993
|
+
for (const n of graph.nodes) {
|
|
3994
|
+
if (!isMM(n)) continue;
|
|
3995
|
+
const w = n.inputs.weight?.shape;
|
|
3996
|
+
const [K, N] = dims(n);
|
|
3997
|
+
if (n.inputs.scale) n.wLayout = "dout";
|
|
3998
|
+
else if (w && w.length >= 2 && K !== N && w[0] === N && w[1] === K) n.wLayout = "dout";
|
|
3999
|
+
else if (w && w.length >= 2 && K !== N && w[0] === K && w[1] === N) n.wLayout = "din";
|
|
4000
|
+
else n.wLayout = model;
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
static _buildFromBlueprint(graphBuilder, config, tensorsMap) {
|
|
4004
|
+
const nodesDef = config.nodes;
|
|
4005
|
+
for (const nodeDef of nodesDef) {
|
|
4006
|
+
const inputs = {};
|
|
4007
|
+
for (const [key, tName] of Object.entries(nodeDef.inputs)) {
|
|
4008
|
+
let t = tensorsMap.get(tName) || graphBuilder.tensors.get(tName);
|
|
4009
|
+
if (!t) {
|
|
4010
|
+
console.warn(`[GraphLoader] Implicitly adding missing graph input '${tName}' with shape [1, 3, 224, 224]`);
|
|
4011
|
+
t = graphBuilder.addInput(tName, [1, 3, 224, 224], "float32");
|
|
4012
|
+
tensorsMap.set(tName, t);
|
|
4013
|
+
}
|
|
4014
|
+
inputs[key] = t;
|
|
4015
|
+
}
|
|
4016
|
+
const outputsShape = nodeDef.outputs_shape || {};
|
|
4017
|
+
const opName = nodeDef.opType || nodeDef.op;
|
|
4018
|
+
const outTensors = graphBuilder.addOp(opName, inputs, outputsShape, nodeDef.params || {});
|
|
4019
|
+
for (const [key, tName] of Object.entries(nodeDef.outputs)) {
|
|
4020
|
+
const t = outTensors[key];
|
|
4021
|
+
if (!t) continue;
|
|
4022
|
+
if (t.name !== tName) {
|
|
4023
|
+
graphBuilder.tensors.delete(t.name);
|
|
4024
|
+
t.name = tName;
|
|
4025
|
+
graphBuilder.tensors.set(tName, t);
|
|
4026
|
+
}
|
|
4027
|
+
tensorsMap.set(tName, t);
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
// Registry for Hugging Face model builders
|
|
4032
|
+
static ModelBuilders = {};
|
|
4033
|
+
static _float16ToFloat32Array(buffer, byteOffset, lengthBytes) {
|
|
4034
|
+
const n = lengthBytes / 2;
|
|
4035
|
+
const view = new DataView(buffer, byteOffset, lengthBytes);
|
|
4036
|
+
const out = new Float32Array(n);
|
|
4037
|
+
for (let i = 0; i < n; i++) out[i] = _GraphLoader._float16BitsToFloat32(view.getUint16(i * 2, true));
|
|
4038
|
+
return out;
|
|
4039
|
+
}
|
|
4040
|
+
static _float16BitsToFloat32(h) {
|
|
4041
|
+
const sign = h & 32768 ? -1 : 1;
|
|
4042
|
+
const exp = h >> 10 & 31;
|
|
4043
|
+
const mant = h & 1023;
|
|
4044
|
+
if (exp === 0) {
|
|
4045
|
+
if (mant === 0) return sign < 0 ? -0 : 0;
|
|
4046
|
+
return sign * Math.pow(2, -14) * (mant / 1024);
|
|
4047
|
+
}
|
|
4048
|
+
if (exp === 31) return mant ? NaN : sign * Infinity;
|
|
4049
|
+
return sign * Math.pow(2, exp - 15) * (1 + mant / 1024);
|
|
4050
|
+
}
|
|
4051
|
+
/**
|
|
4052
|
+
* Conv kernels (JS and WASM) expect float32 weights and have no QConv path, so
|
|
4053
|
+
* fold per-output-channel int8 scales into the weights up front. MatMul keeps
|
|
4054
|
+
* its int8+scale fast path and is left untouched.
|
|
4055
|
+
*/
|
|
4056
|
+
static _dequantizeConvWeights(graph) {
|
|
4057
|
+
for (const node of graph.nodes) {
|
|
4058
|
+
if (node.opType !== "Conv2D" && node.opType !== "Conv1D" && node.opType !== "QConv2D") continue;
|
|
4059
|
+
const w = node.inputs.weight;
|
|
4060
|
+
const s = node.inputs.scale || node.inputs.weight_scale;
|
|
4061
|
+
if (!s || !w || w.dtype !== "int8" || !w.buffer) continue;
|
|
4062
|
+
const zp = node.inputs.weight_zero_point;
|
|
4063
|
+
const outC = w.shape[0];
|
|
4064
|
+
const perOut = w.buffer.length / outC;
|
|
4065
|
+
const deq = new Float32Array(w.buffer.length);
|
|
4066
|
+
for (let oc = 0; oc < outC; oc++) {
|
|
4067
|
+
const sc = s.buffer[oc];
|
|
4068
|
+
const z = zp && zp.buffer ? zp.buffer[zp.buffer.length === 1 ? 0 : oc] : 0;
|
|
4069
|
+
const base = oc * perOut;
|
|
4070
|
+
for (let j = 0; j < perOut; j++) deq[base + j] = (w.buffer[base + j] - z) * sc;
|
|
4071
|
+
}
|
|
4072
|
+
if (node.opType === "QConv2D") {
|
|
4073
|
+
const deqWeight = graph.addWeight(`${w.name}__deq_${node.id}`, w.shape, "float32");
|
|
4074
|
+
deqWeight.buffer = deq;
|
|
4075
|
+
deqWeight.sizeBytes = deq.length * 4;
|
|
4076
|
+
node.inputs.weight = deqWeight;
|
|
4077
|
+
} else {
|
|
4078
|
+
w.buffer = deq;
|
|
4079
|
+
w.dtype = "float32";
|
|
4080
|
+
w.sizeBytes = deq.length * 4;
|
|
4081
|
+
}
|
|
4082
|
+
delete node.inputs.scale;
|
|
4083
|
+
delete node.inputs.weight_scale;
|
|
4084
|
+
delete node.inputs.weight_zero_point;
|
|
4085
|
+
if (node.opType === "QConv2D") node.opType = "Conv2D";
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
static _normalizeConvWeightsForImageLayout(graph) {
|
|
4089
|
+
for (const node of graph.nodes) {
|
|
4090
|
+
if (node.opType !== "Conv2D") continue;
|
|
4091
|
+
const w = node.inputs.weight;
|
|
4092
|
+
const input = node.inputs.input;
|
|
4093
|
+
const output = node.outputs.out;
|
|
4094
|
+
if (!w || !w.buffer || !input || !output || w.shape.length !== 4) continue;
|
|
4095
|
+
const layout = node.params?.weight_layout || "HWIO";
|
|
4096
|
+
if (layout === "HWIO" || layout === "HWCM") continue;
|
|
4097
|
+
if (layout === "OHWI" || layout === "OIHW") {
|
|
4098
|
+
const [ocN, a, b, c] = w.shape;
|
|
4099
|
+
const kh = layout === "OHWI" ? a : b;
|
|
4100
|
+
const kw = layout === "OHWI" ? b : c;
|
|
4101
|
+
const icN = layout === "OHWI" ? c : a;
|
|
4102
|
+
const src = w.buffer;
|
|
4103
|
+
const dst = new Float32Array(src.length);
|
|
4104
|
+
for (let oc = 0; oc < ocN; oc++) {
|
|
4105
|
+
for (let y = 0; y < kh; y++) {
|
|
4106
|
+
for (let x = 0; x < kw; x++) {
|
|
4107
|
+
for (let ic = 0; ic < icN; ic++) {
|
|
4108
|
+
dst[((y * kw + x) * icN + ic) * ocN + oc] = layout === "OHWI" ? src[((oc * kh + y) * kw + x) * icN + ic] : src[((oc * icN + ic) * kh + y) * kw + x];
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
w.buffer = dst;
|
|
4114
|
+
w.shape = [kh, kw, icN, ocN];
|
|
4115
|
+
w.sizeBytes = dst.length * 4;
|
|
4116
|
+
node.params.weight_layout = "HWIO";
|
|
4117
|
+
} else if (layout === "1HWO" || layout === "1HWM") {
|
|
4118
|
+
const [, kh, kw, ocN] = w.shape;
|
|
4119
|
+
const icN = input.shape[3];
|
|
4120
|
+
const mult = output.shape[3] / icN;
|
|
4121
|
+
if (!Number.isInteger(mult) || mult <= 0 || icN * mult !== ocN) {
|
|
4122
|
+
throw new Error(`[GraphLoader] Invalid depthwise Conv2D shape for node ${node.id}`);
|
|
4123
|
+
}
|
|
4124
|
+
const src = w.buffer;
|
|
4125
|
+
const dst = new Float32Array(src.length);
|
|
4126
|
+
for (let y = 0; y < kh; y++) {
|
|
4127
|
+
for (let x = 0; x < kw; x++) {
|
|
4128
|
+
for (let ic = 0; ic < icN; ic++) {
|
|
4129
|
+
for (let m = 0; m < mult; m++) {
|
|
4130
|
+
dst[((y * kw + x) * icN + ic) * mult + m] = src[(y * kw + x) * ocN + ic * mult + m];
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
w.buffer = dst;
|
|
4136
|
+
w.shape = [kh, kw, icN, mult];
|
|
4137
|
+
w.sizeBytes = dst.length * 4;
|
|
4138
|
+
node.params.weight_layout = "HWCM";
|
|
4139
|
+
} else {
|
|
4140
|
+
throw new Error(`[GraphLoader] Unsupported Conv2D weight_layout '${layout}'. VolvoxAI uses NHWC/HWIO only.`);
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
};
|
|
4145
|
+
|
|
4146
|
+
// js/WebNNEngine.js
|
|
4147
|
+
var WebNNEngine = class {
|
|
4148
|
+
constructor(context) {
|
|
4149
|
+
this.context = context;
|
|
4150
|
+
this.graph = null;
|
|
4151
|
+
this.compiledGraph = null;
|
|
4152
|
+
this.operands = {};
|
|
4153
|
+
this.inputs = [];
|
|
4154
|
+
this.outputs = [];
|
|
4155
|
+
}
|
|
4156
|
+
async allocateGraph(graph) {
|
|
4157
|
+
this.graph = graph;
|
|
4158
|
+
this.operands = {};
|
|
4159
|
+
this.inputs = [];
|
|
4160
|
+
this.outputs = [];
|
|
4161
|
+
const builder = new MLGraphBuilder(this.context);
|
|
4162
|
+
const desc = (shape) => {
|
|
4163
|
+
const d = shape.length ? shape : [1];
|
|
4164
|
+
return { dataType: "float32", type: "float32", shape: d, dimensions: d };
|
|
4165
|
+
};
|
|
4166
|
+
const generated = /* @__PURE__ */ new Set();
|
|
4167
|
+
for (const node of graph.nodes)
|
|
4168
|
+
for (const t of Object.values(node.outputs)) if (t && t.name) generated.add(t.name);
|
|
4169
|
+
for (const t of graph.tensors.values()) {
|
|
4170
|
+
if (t.isWeight && t.buffer) {
|
|
4171
|
+
this.operands[t.name] = builder.constant(desc(t.shape), t.buffer);
|
|
4172
|
+
generated.add(t.name);
|
|
4173
|
+
} else if (!generated.has(t.name)) {
|
|
4174
|
+
this.inputs.push(t.name);
|
|
4175
|
+
this.operands[t.name] = builder.input(t.name, desc(t.shape));
|
|
4176
|
+
generated.add(t.name);
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
const getOp = (name) => {
|
|
4180
|
+
if (!name || !this.operands[name]) {
|
|
4181
|
+
console.warn(`[WebNN] missing operand: ${name}`);
|
|
4182
|
+
this.operands[name] = builder.constant(desc([1]), new Float32Array([0]));
|
|
4183
|
+
}
|
|
4184
|
+
return this.operands[name];
|
|
4185
|
+
};
|
|
4186
|
+
const nin = (node, key) => node.inputs[key] ? node.inputs[key].name : null;
|
|
4187
|
+
for (const node of graph.nodes) {
|
|
4188
|
+
const op = node.opType;
|
|
4189
|
+
const outName = Object.values(node.outputs)[0].name;
|
|
4190
|
+
try {
|
|
4191
|
+
if (op === "MatMul" || op === "Linear" || op === "Gemm") {
|
|
4192
|
+
const a = getOp(nin(node, "input") || nin(node, "a"));
|
|
4193
|
+
const w = getOp(nin(node, "weight") || nin(node, "b"));
|
|
4194
|
+
let res = node.wLayout === "dout" ? builder.gemm(a, w, { bTranspose: true }) : builder.matmul(a, w);
|
|
4195
|
+
if (nin(node, "bias")) res = builder.add(res, getOp(nin(node, "bias")));
|
|
4196
|
+
this.operands[outName] = res;
|
|
4197
|
+
} else if (op === "Add") {
|
|
4198
|
+
this.operands[outName] = builder.add(getOp(nin(node, "a") || nin(node, "input")), getOp(nin(node, "b")));
|
|
4199
|
+
} else if (op === "Mul") {
|
|
4200
|
+
this.operands[outName] = builder.mul(getOp(nin(node, "a") || nin(node, "input")), getOp(nin(node, "b")));
|
|
4201
|
+
} else if (op === "ReLU") {
|
|
4202
|
+
this.operands[outName] = builder.relu(getOp(nin(node, "input")));
|
|
4203
|
+
} else if (op === "GELU") {
|
|
4204
|
+
this.operands[outName] = builder.gelu(getOp(nin(node, "input")));
|
|
4205
|
+
} else if (op === "SiLU" || op === "Swish") {
|
|
4206
|
+
const x = getOp(nin(node, "input"));
|
|
4207
|
+
this.operands[outName] = builder.mul(x, builder.sigmoid(x));
|
|
4208
|
+
} else if (op === "Sigmoid") {
|
|
4209
|
+
this.operands[outName] = builder.sigmoid(getOp(nin(node, "input")));
|
|
4210
|
+
} else if (op === "Softmax") {
|
|
4211
|
+
this.operands[outName] = builder.softmax(getOp(nin(node, "input")));
|
|
4212
|
+
} else if (op === "Reshape" || op === "Flatten") {
|
|
4213
|
+
const shape = Object.values(node.outputs)[0].shape;
|
|
4214
|
+
this.operands[outName] = builder.reshape(getOp(nin(node, "input")), shape.length ? shape : [1]);
|
|
4215
|
+
} else if (op === "LayerNorm") {
|
|
4216
|
+
const input = getOp(nin(node, "input"));
|
|
4217
|
+
const scale = nin(node, "weight") ? getOp(nin(node, "weight")) : void 0;
|
|
4218
|
+
const bias = nin(node, "bias") ? getOp(nin(node, "bias")) : void 0;
|
|
4219
|
+
const inShape = node.inputs.input.shape;
|
|
4220
|
+
this.operands[outName] = builder.layerNormalization(input, { axes: [inShape.length - 1], scale, bias });
|
|
4221
|
+
} else if (op === "Conv2D") {
|
|
4222
|
+
this.operands[outName] = builder.conv2d(
|
|
4223
|
+
getOp(nin(node, "input")),
|
|
4224
|
+
getOp(nin(node, "weight")),
|
|
4225
|
+
{
|
|
4226
|
+
bias: nin(node, "bias") ? getOp(nin(node, "bias")) : void 0,
|
|
4227
|
+
strides: node.params.stride,
|
|
4228
|
+
padding: node.params.padding,
|
|
4229
|
+
groups: node.params.groups || 1
|
|
4230
|
+
}
|
|
4231
|
+
);
|
|
4232
|
+
} else if (op === "Embedding") {
|
|
4233
|
+
const idx = builder.cast(getOp(nin(node, "input")), "int32");
|
|
4234
|
+
this.operands[outName] = builder.gather(getOp(nin(node, "weight")), idx, { axis: 0 });
|
|
4235
|
+
} else if (op === "SDPA") {
|
|
4236
|
+
const qkvT = node.inputs.qkv;
|
|
4237
|
+
const seq = qkvT.shape[1];
|
|
4238
|
+
const d = Math.floor(qkvT.shape[2] / 3);
|
|
4239
|
+
const heads = node.params.heads || 8;
|
|
4240
|
+
const hd = Math.floor(d / heads);
|
|
4241
|
+
const scale = node.params.scale !== void 0 ? node.params.scale : 1 / Math.sqrt(hd);
|
|
4242
|
+
const qkv = builder.reshape(getOp(nin(node, "qkv")), [seq, 3 * d]);
|
|
4243
|
+
const Q = builder.slice(qkv, [0, 0], [seq, d]);
|
|
4244
|
+
const K = builder.slice(qkv, [0, d], [seq, d]);
|
|
4245
|
+
const V = builder.slice(qkv, [0, 2 * d], [seq, d]);
|
|
4246
|
+
const toHeads = (x) => builder.transpose(builder.reshape(x, [seq, heads, hd]), { permutation: [1, 0, 2] });
|
|
4247
|
+
const Qh = toHeads(Q), Vh = toHeads(V);
|
|
4248
|
+
const Kh = builder.transpose(builder.reshape(K, [seq, heads, hd]), { permutation: [1, 2, 0] });
|
|
4249
|
+
let scores = builder.matmul(Qh, Kh);
|
|
4250
|
+
scores = builder.mul(scores, builder.constant(desc([1]), new Float32Array([scale])));
|
|
4251
|
+
const mask = new Float32Array(seq * seq);
|
|
4252
|
+
for (let i = 0; i < seq; i++) for (let j = 0; j < seq; j++) mask[i * seq + j] = j <= i ? 0 : -1e9;
|
|
4253
|
+
scores = builder.add(scores, builder.constant(desc([seq, seq]), mask));
|
|
4254
|
+
const attn = builder.softmax(scores, 2);
|
|
4255
|
+
let out = builder.matmul(attn, Vh);
|
|
4256
|
+
out = builder.transpose(out, { permutation: [1, 0, 2] });
|
|
4257
|
+
this.operands[outName] = builder.reshape(out, [1, seq, d]);
|
|
4258
|
+
} else {
|
|
4259
|
+
throw new Error(`Unsupported op in WebNNEngine: ${op}`);
|
|
4260
|
+
}
|
|
4261
|
+
} catch (e) {
|
|
4262
|
+
console.warn(`[WebNN] cannot map op ${op}: ${e.message}`);
|
|
4263
|
+
throw e;
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
const outputOperands = {};
|
|
4267
|
+
for (const name of graph.outputNames) {
|
|
4268
|
+
if (this.operands[name]) {
|
|
4269
|
+
outputOperands[name] = this.operands[name];
|
|
4270
|
+
this.outputs.push(name);
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
console.log(`[WebNN] building graph, outputs:`, this.outputs);
|
|
4274
|
+
this.compiledGraph = await builder.build(outputOperands);
|
|
4275
|
+
console.log(`[WebNN] graph compiled.`);
|
|
4276
|
+
return this;
|
|
4277
|
+
}
|
|
4278
|
+
async execute(inputsMap) {
|
|
4279
|
+
if (!this.compiledGraph) throw new Error("WebNN graph not compiled.");
|
|
4280
|
+
const ctx = this.context;
|
|
4281
|
+
const numel = (name) => {
|
|
4282
|
+
const t = this.graph.tensors.get(name);
|
|
4283
|
+
return t ? t.shape.reduce((a, b) => a * b, 1) : 1;
|
|
4284
|
+
};
|
|
4285
|
+
const shapeOf = (name) => {
|
|
4286
|
+
const t = this.graph.tensors.get(name);
|
|
4287
|
+
return t && t.shape.length ? t.shape : [1];
|
|
4288
|
+
};
|
|
4289
|
+
if (typeof ctx.compute === "function") {
|
|
4290
|
+
const ins = {}, outs = {};
|
|
4291
|
+
for (const name of this.inputs) ins[name] = inputsMap[name] || new Float32Array(numel(name));
|
|
4292
|
+
for (const name of this.outputs) outs[name] = new Float32Array(numel(name));
|
|
4293
|
+
return (await ctx.compute(this.compiledGraph, ins, outs)).outputs;
|
|
4294
|
+
}
|
|
4295
|
+
const inT = {}, outT = {};
|
|
4296
|
+
for (const name of this.inputs) {
|
|
4297
|
+
const t = await ctx.createTensor({ dataType: "float32", shape: shapeOf(name), dimensions: shapeOf(name), writable: true });
|
|
4298
|
+
ctx.writeTensor(t, inputsMap[name] || new Float32Array(numel(name)));
|
|
4299
|
+
inT[name] = t;
|
|
4300
|
+
}
|
|
4301
|
+
for (const name of this.outputs)
|
|
4302
|
+
outT[name] = await ctx.createTensor({ dataType: "float32", shape: shapeOf(name), dimensions: shapeOf(name), readable: true });
|
|
4303
|
+
ctx.dispatch(this.compiledGraph, inT, outT);
|
|
4304
|
+
const results = {};
|
|
4305
|
+
for (const name of this.outputs) {
|
|
4306
|
+
const ab = await ctx.readTensor(outT[name]);
|
|
4307
|
+
results[name] = new Float32Array(ab);
|
|
4308
|
+
outT[name].destroy?.();
|
|
4309
|
+
}
|
|
4310
|
+
for (const name of this.inputs) inT[name].destroy?.();
|
|
4311
|
+
return results;
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4314
|
+
|
|
4315
|
+
// js/VolvoxAI.js
|
|
4316
|
+
var VolvoxAI = class _VolvoxAI {
|
|
4317
|
+
constructor() {
|
|
4318
|
+
this.engines = [];
|
|
4319
|
+
this.weightsBaseUrl = null;
|
|
4320
|
+
}
|
|
4321
|
+
static async init(preferredBackend = "auto", wasmUrl = "./volvoxai.wasm") {
|
|
4322
|
+
const instance = new _VolvoxAI();
|
|
4323
|
+
const stringOrders = {
|
|
4324
|
+
auto: ["webnn", "webgpu", "wasm", "cpu"],
|
|
4325
|
+
webnn: ["webnn", "wasm", "cpu"],
|
|
4326
|
+
webgpu: ["webgpu", "wasm", "cpu"],
|
|
4327
|
+
wasm: ["wasm", "cpu"],
|
|
4328
|
+
cpu: ["cpu"]
|
|
4329
|
+
};
|
|
4330
|
+
const validBackends = /* @__PURE__ */ new Set(["webnn", "webgpu", "wasm", "cpu"]);
|
|
4331
|
+
const strictList = Array.isArray(preferredBackend);
|
|
4332
|
+
const order = strictList ? [...preferredBackend] : stringOrders[preferredBackend];
|
|
4333
|
+
if (!order || order.length === 0) {
|
|
4334
|
+
throw new Error(`[VolvoxAI] Invalid backend selection: ${JSON.stringify(preferredBackend)}`);
|
|
4335
|
+
}
|
|
4336
|
+
for (const backend of order) {
|
|
4337
|
+
if (!validBackends.has(backend)) {
|
|
4338
|
+
throw new Error(`[VolvoxAI] Unknown backend '${backend}'. Use 'auto', 'webnn', 'webgpu', 'wasm', 'cpu', or an array of those backend names.`);
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
const added = /* @__PURE__ */ new Set();
|
|
4342
|
+
for (const backend of order) {
|
|
4343
|
+
if (added.has(backend)) continue;
|
|
4344
|
+
if (backend === "webnn") {
|
|
4345
|
+
if (typeof navigator !== "undefined" && navigator.ml) {
|
|
4346
|
+
try {
|
|
4347
|
+
const context = await navigator.ml.createContext({ deviceType: "npu" });
|
|
4348
|
+
console.log("[VolvoxAI] WebNN Engine (NPU) Initialized successfully.");
|
|
4349
|
+
instance.engines.push({ type: "webnn", engine: new WebNNEngine(context) });
|
|
4350
|
+
added.add(backend);
|
|
4351
|
+
} catch (e) {
|
|
4352
|
+
console.warn("[VolvoxAI] WebNN initialization failed.", e.message);
|
|
4353
|
+
}
|
|
4354
|
+
} else if (strictList || preferredBackend === "webnn") {
|
|
4355
|
+
console.warn("[VolvoxAI] WebNN is not supported.");
|
|
4356
|
+
}
|
|
4357
|
+
} else if (backend === "webgpu") {
|
|
4358
|
+
if (typeof navigator !== "undefined" && navigator.gpu) {
|
|
4359
|
+
try {
|
|
4360
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
4361
|
+
if (adapter) {
|
|
4362
|
+
const device = await adapter.requestDevice();
|
|
4363
|
+
console.log("[VolvoxAI] WebGPU Engine Initialized successfully.");
|
|
4364
|
+
instance.engines.push({ type: "webgpu", device });
|
|
4365
|
+
added.add(backend);
|
|
4366
|
+
} else {
|
|
4367
|
+
console.warn("[VolvoxAI] WebGPU adapter is not available.");
|
|
4368
|
+
}
|
|
4369
|
+
} catch (e) {
|
|
4370
|
+
console.warn("[VolvoxAI] WebGPU initialization failed.", e.message);
|
|
4371
|
+
}
|
|
4372
|
+
} else if (strictList || preferredBackend === "webgpu") {
|
|
4373
|
+
console.warn("[VolvoxAI] WebGPU is not supported.");
|
|
4374
|
+
}
|
|
4375
|
+
} else if (backend === "wasm") {
|
|
4376
|
+
const wasmEngine = await WasmEngine.init(wasmUrl);
|
|
4377
|
+
if (wasmEngine) {
|
|
4378
|
+
console.log("[VolvoxAI] WASM Engine Initialized successfully.");
|
|
4379
|
+
instance.engines.push({ type: "wasm", engine: wasmEngine });
|
|
4380
|
+
added.add(backend);
|
|
4381
|
+
} else {
|
|
4382
|
+
console.warn("[VolvoxAI] WASM initialization failed.");
|
|
4383
|
+
}
|
|
4384
|
+
} else if (backend === "cpu") {
|
|
4385
|
+
console.log("[VolvoxAI] Pure JS CPU Engine Initialized.");
|
|
4386
|
+
instance.engines.push({ type: "cpu", engine: new CPUEngine() });
|
|
4387
|
+
added.add(backend);
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
if (instance.engines.length === 0) {
|
|
4391
|
+
throw new Error(`[VolvoxAI] None of the requested backends initialized: ${order.join(", ")}`);
|
|
4392
|
+
}
|
|
4393
|
+
return instance;
|
|
4394
|
+
}
|
|
4395
|
+
createGraph() {
|
|
4396
|
+
return new Graph();
|
|
4397
|
+
}
|
|
4398
|
+
async loadGraph(safetensorsUrl) {
|
|
4399
|
+
const graph = this.createGraph();
|
|
4400
|
+
return await GraphLoader.load(graph, safetensorsUrl);
|
|
4401
|
+
}
|
|
4402
|
+
async compile(graph, weightsUrl) {
|
|
4403
|
+
this.weightsBaseUrl = weightsUrl;
|
|
4404
|
+
console.log(`[VolvoxAI] Compiling graph with ${graph.nodes.length} nodes...`);
|
|
4405
|
+
for (const entry of this.engines) {
|
|
4406
|
+
try {
|
|
4407
|
+
if (entry.type === "webgpu") {
|
|
4408
|
+
console.log(`[VolvoxAI] Trying to allocate graph on WebGPU (Tier 2)...`);
|
|
4409
|
+
const executor = new GraphExecutor(entry.device, graph);
|
|
4410
|
+
await executor.compile();
|
|
4411
|
+
console.log(`[VolvoxAI] WebGPU Engine compiled successfully.`);
|
|
4412
|
+
return executor;
|
|
4413
|
+
} else {
|
|
4414
|
+
console.log(`[VolvoxAI] Trying to allocate graph on ${entry.engine.constructor.name}...`);
|
|
4415
|
+
await entry.engine.allocateGraph(graph);
|
|
4416
|
+
console.log(`[VolvoxAI] ${entry.engine.constructor.name} compiled successfully.`);
|
|
4417
|
+
return entry.engine;
|
|
4418
|
+
}
|
|
4419
|
+
} catch (e) {
|
|
4420
|
+
console.warn(`[VolvoxAI] Compilation failed on ${entry.type}. Falling back to next tier. Error: ${e.message}`);
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
throw new Error("[VolvoxAI] All engine tiers failed to compile the graph.");
|
|
4424
|
+
}
|
|
4425
|
+
};
|
|
4426
|
+
|
|
4427
|
+
// js/Tokenizer.js
|
|
4428
|
+
var Tokenizer = class {
|
|
4429
|
+
constructor() {
|
|
4430
|
+
this.vocabByText = /* @__PURE__ */ new Map();
|
|
4431
|
+
this.vocabByLatin1 = /* @__PURE__ */ new Map();
|
|
4432
|
+
this.idToText = [];
|
|
4433
|
+
this.idToBytes = [];
|
|
4434
|
+
this.merges = /* @__PURE__ */ new Map();
|
|
4435
|
+
this.mergeEnabled = false;
|
|
4436
|
+
this.decoder = new TextDecoder("utf-8");
|
|
4437
|
+
this.encoder = new TextEncoder();
|
|
4438
|
+
this.bpePattern = /'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+/gu;
|
|
4439
|
+
}
|
|
4440
|
+
// vocab.json and vocab.bin are both supported.
|
|
4441
|
+
// pass vocabUrl, mergesUrl.
|
|
4442
|
+
async load(vocabUrl, mergesUrl = null) {
|
|
4443
|
+
try {
|
|
4444
|
+
const isBin = /\.bin$/i.test(vocabUrl);
|
|
4445
|
+
if (isBin) await this.loadFromBinary(vocabUrl);
|
|
4446
|
+
else await this.loadFromJson(vocabUrl);
|
|
4447
|
+
if (mergesUrl) await this.loadMerges(mergesUrl);
|
|
4448
|
+
} catch (err) {
|
|
4449
|
+
console.error("Tokenizer load error:", err);
|
|
4450
|
+
throw err;
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
resetVocab() {
|
|
4454
|
+
this.vocabByText.clear();
|
|
4455
|
+
this.vocabByLatin1.clear();
|
|
4456
|
+
this.idToText = [];
|
|
4457
|
+
this.idToBytes = [];
|
|
4458
|
+
this.merges.clear();
|
|
4459
|
+
this.mergeEnabled = false;
|
|
4460
|
+
}
|
|
4461
|
+
async loadFromJson(vocabUrl) {
|
|
4462
|
+
const res = await fetch(vocabUrl);
|
|
4463
|
+
if (!res.ok) throw new Error(`Failed to fetch vocab: ${res.statusText}`);
|
|
4464
|
+
const vocabJson = await res.json();
|
|
4465
|
+
this.resetVocab();
|
|
4466
|
+
for (const [text, id] of Object.entries(vocabJson)) {
|
|
4467
|
+
const bytes = this.encoder.encode(text);
|
|
4468
|
+
this.vocabByText.set(text, id);
|
|
4469
|
+
this.vocabByLatin1.set(this.bytesToLatin1(bytes), id);
|
|
4470
|
+
this.idToText[id] = text;
|
|
4471
|
+
this.idToBytes[id] = bytes;
|
|
4472
|
+
}
|
|
4473
|
+
console.log(`Loaded ${Object.keys(vocabJson).length} tokens from ${vocabUrl}`);
|
|
4474
|
+
}
|
|
4475
|
+
async loadFromBinary(vocabUrl) {
|
|
4476
|
+
const res = await fetch(vocabUrl);
|
|
4477
|
+
if (!res.ok) throw new Error(`Failed to fetch vocab.bin: ${res.statusText}`);
|
|
4478
|
+
const buf = new Uint8Array(await res.arrayBuffer());
|
|
4479
|
+
if (buf.length < 4) throw new Error("Invalid vocab.bin");
|
|
4480
|
+
const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
4481
|
+
const size = dv.getInt32(0, true);
|
|
4482
|
+
this.resetVocab();
|
|
4483
|
+
let off = 4;
|
|
4484
|
+
for (let i = 0; i < size; i++) {
|
|
4485
|
+
if (off + 4 > buf.length) throw new Error(`vocab.bin truncated at token ${i}`);
|
|
4486
|
+
const len = dv.getInt32(off, true);
|
|
4487
|
+
off += 4;
|
|
4488
|
+
if (len < 0 || off + len > buf.length) throw new Error(`vocab.bin malformed at token ${i}`);
|
|
4489
|
+
const tokenBytes = buf.slice(off, off + len);
|
|
4490
|
+
off += len;
|
|
4491
|
+
const text = this.decoder.decode(tokenBytes);
|
|
4492
|
+
this.vocabByText.set(text, i);
|
|
4493
|
+
this.vocabByLatin1.set(this.bytesToLatin1(tokenBytes), i);
|
|
4494
|
+
this.idToText[i] = text;
|
|
4495
|
+
this.idToBytes[i] = tokenBytes;
|
|
4496
|
+
}
|
|
4497
|
+
console.log(`Loaded ${size} tokens from ${vocabUrl}`);
|
|
4498
|
+
}
|
|
4499
|
+
async loadMerges(mergesUrl) {
|
|
4500
|
+
const res = await fetch(mergesUrl);
|
|
4501
|
+
if (!res.ok) throw new Error(`Failed to fetch merges: ${res.statusText}`);
|
|
4502
|
+
const text = await res.text();
|
|
4503
|
+
let rank = 0;
|
|
4504
|
+
this.merges.clear();
|
|
4505
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
4506
|
+
const line = rawLine.trim();
|
|
4507
|
+
if (!line || line.startsWith("#")) continue;
|
|
4508
|
+
const parts = line.split(/\s+/);
|
|
4509
|
+
if (parts.length < 2) continue;
|
|
4510
|
+
const leftToken = this.normalizeMergeToken(parts[0]);
|
|
4511
|
+
const rightToken = this.normalizeMergeToken(parts[1]);
|
|
4512
|
+
const leftId = this.findTokenByText(leftToken);
|
|
4513
|
+
const rightId = this.findTokenByText(rightToken);
|
|
4514
|
+
if (leftId < 0 || rightId < 0) continue;
|
|
4515
|
+
const mergedText = `${this.idToText[leftId]}${this.idToText[rightId]}`;
|
|
4516
|
+
const mergedId = this.vocabByText.get(mergedText);
|
|
4517
|
+
if (mergedId === void 0) continue;
|
|
4518
|
+
const key = this.pairKey(leftId, rightId);
|
|
4519
|
+
if (!this.merges.has(key)) {
|
|
4520
|
+
this.merges.set(key, { rank: rank++, mergedId });
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
this.mergeEnabled = this.merges.size > 0;
|
|
4524
|
+
console.log(`Loaded ${this.merges.size} merge rules from ${mergesUrl}`);
|
|
4525
|
+
}
|
|
4526
|
+
bytesToLatin1(bytes) {
|
|
4527
|
+
let out = "";
|
|
4528
|
+
for (let i = 0; i < bytes.length; i++) out += String.fromCharCode(bytes[i]);
|
|
4529
|
+
return out;
|
|
4530
|
+
}
|
|
4531
|
+
utf8Len(byte) {
|
|
4532
|
+
if (byte < 128) return 1;
|
|
4533
|
+
if ((byte & 224) === 192) return 2;
|
|
4534
|
+
if ((byte & 240) === 224) return 3;
|
|
4535
|
+
if ((byte & 248) === 240) return 4;
|
|
4536
|
+
return 1;
|
|
4537
|
+
}
|
|
4538
|
+
pairKey(left, right) {
|
|
4539
|
+
return `${left},${right}`;
|
|
4540
|
+
}
|
|
4541
|
+
normalizeMergeToken(token) {
|
|
4542
|
+
if (token.startsWith("\u0120")) return ` ${token.slice(1)}`;
|
|
4543
|
+
return token;
|
|
4544
|
+
}
|
|
4545
|
+
findTokenByText(text) {
|
|
4546
|
+
let id = this.vocabByText.get(text);
|
|
4547
|
+
if (id !== void 0) return id;
|
|
4548
|
+
id = this.vocabByLatin1.get(text);
|
|
4549
|
+
return id === void 0 ? -1 : id;
|
|
4550
|
+
}
|
|
4551
|
+
findTokenByBytes(bytes) {
|
|
4552
|
+
const text = this.decoder.decode(bytes);
|
|
4553
|
+
let id = this.vocabByText.get(text);
|
|
4554
|
+
if (id !== void 0) return id;
|
|
4555
|
+
id = this.vocabByLatin1.get(this.bytesToLatin1(bytes));
|
|
4556
|
+
return id === void 0 ? -1 : id;
|
|
4557
|
+
}
|
|
4558
|
+
// Decode a single token ID to its text representation.
|
|
4559
|
+
decodeToken(tokenId) {
|
|
4560
|
+
const bytes = this.idToBytes[tokenId];
|
|
4561
|
+
if (!bytes) return "";
|
|
4562
|
+
return this.decoder.decode(bytes).replace(/Ġ/g, " ");
|
|
4563
|
+
}
|
|
4564
|
+
// Decode a token-id array.
|
|
4565
|
+
decode(tokenIds) {
|
|
4566
|
+
return tokenIds.map((id) => this.decodeToken(id)).join("");
|
|
4567
|
+
}
|
|
4568
|
+
encodeBpeWord(word, maxTokens) {
|
|
4569
|
+
const bytes = this.encoder.encode(word);
|
|
4570
|
+
if (bytes.length === 0 || maxTokens <= 0) return [];
|
|
4571
|
+
const symbols = [];
|
|
4572
|
+
for (let pos = 0; pos < bytes.length; ) {
|
|
4573
|
+
let clen = this.utf8Len(bytes[pos]);
|
|
4574
|
+
if (pos + clen > bytes.length) clen = bytes.length - pos;
|
|
4575
|
+
const symbolBytes = bytes.slice(pos, pos + clen);
|
|
4576
|
+
const id = this.findTokenByBytes(symbolBytes);
|
|
4577
|
+
if (id >= 0) {
|
|
4578
|
+
symbols.push(id);
|
|
4579
|
+
} else {
|
|
4580
|
+
for (let i = 0; i < clen; i++) {
|
|
4581
|
+
const byteId = this.findTokenByBytes(Uint8Array.of(bytes[pos + i]));
|
|
4582
|
+
if (byteId >= 0) symbols.push(byteId);
|
|
4583
|
+
}
|
|
4584
|
+
}
|
|
4585
|
+
pos += clen;
|
|
4586
|
+
}
|
|
4587
|
+
while (symbols.length > 1) {
|
|
4588
|
+
let bestIdx = -1;
|
|
4589
|
+
let bestRank = Number.MAX_SAFE_INTEGER;
|
|
4590
|
+
let bestMerged = -1;
|
|
4591
|
+
for (let i = 0; i < symbols.length - 1; i++) {
|
|
4592
|
+
const merge = this.merges.get(this.pairKey(symbols[i], symbols[i + 1]));
|
|
4593
|
+
if (!merge) continue;
|
|
4594
|
+
if (merge.rank >= bestRank) continue;
|
|
4595
|
+
if (merge.mergedId < 0) continue;
|
|
4596
|
+
bestIdx = i;
|
|
4597
|
+
bestRank = merge.rank;
|
|
4598
|
+
bestMerged = merge.mergedId;
|
|
4599
|
+
}
|
|
4600
|
+
if (bestIdx < 0) break;
|
|
4601
|
+
symbols[bestIdx] = bestMerged;
|
|
4602
|
+
symbols.splice(bestIdx + 1, 1);
|
|
4603
|
+
}
|
|
4604
|
+
return symbols.slice(0, maxTokens);
|
|
4605
|
+
}
|
|
4606
|
+
encodeGreedy(text, maxTokens) {
|
|
4607
|
+
const bytes = this.encoder.encode(text);
|
|
4608
|
+
const tokens = [];
|
|
4609
|
+
let pos = 0;
|
|
4610
|
+
while (pos < bytes.length && tokens.length < maxTokens) {
|
|
4611
|
+
let bestId = -1;
|
|
4612
|
+
let bestLen = 0;
|
|
4613
|
+
for (let i = 0; i < this.idToBytes.length; i++) {
|
|
4614
|
+
const tokenBytes = this.idToBytes[i];
|
|
4615
|
+
if (!tokenBytes || tokenBytes.length <= bestLen || pos + tokenBytes.length > bytes.length) continue;
|
|
4616
|
+
let match = true;
|
|
4617
|
+
for (let j = 0; j < tokenBytes.length; j++) {
|
|
4618
|
+
if (bytes[pos + j] !== tokenBytes[j]) {
|
|
4619
|
+
match = false;
|
|
4620
|
+
break;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
if (match) {
|
|
4624
|
+
bestId = i;
|
|
4625
|
+
bestLen = tokenBytes.length;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
if (bestId < 0) {
|
|
4629
|
+
pos++;
|
|
4630
|
+
continue;
|
|
4631
|
+
}
|
|
4632
|
+
tokens.push(bestId);
|
|
4633
|
+
pos += bestLen;
|
|
4634
|
+
}
|
|
4635
|
+
return tokens;
|
|
4636
|
+
}
|
|
4637
|
+
// Tokenize with GPT-2-style regex pretokenization and run one BPE pass per chunk.
|
|
4638
|
+
encode(text, maxTokens = 256) {
|
|
4639
|
+
if (maxTokens <= 0) return [];
|
|
4640
|
+
if (this.vocabByText.size === 0 && this.vocabByLatin1.size === 0) return [];
|
|
4641
|
+
if (!this.mergeEnabled) return this.encodeGreedy(text, maxTokens);
|
|
4642
|
+
const tokens = [];
|
|
4643
|
+
for (const match of text.matchAll(this.bpePattern)) {
|
|
4644
|
+
const chunk = match[0];
|
|
4645
|
+
if (!chunk || tokens.length >= maxTokens) continue;
|
|
4646
|
+
const remain = maxTokens - tokens.length;
|
|
4647
|
+
if (remain <= 0) break;
|
|
4648
|
+
const part = this.encodeBpeWord(chunk, remain);
|
|
4649
|
+
for (const id of part) tokens.push(id);
|
|
4650
|
+
}
|
|
4651
|
+
return tokens;
|
|
4652
|
+
}
|
|
4653
|
+
};
|
|
4654
|
+
export {
|
|
4655
|
+
CPUEngine,
|
|
4656
|
+
Graph,
|
|
4657
|
+
GraphExecutor,
|
|
4658
|
+
GraphLoader,
|
|
4659
|
+
Tensor,
|
|
4660
|
+
Tokenizer,
|
|
4661
|
+
VolvoxAI,
|
|
4662
|
+
WasmEngine,
|
|
4663
|
+
_pair
|
|
4664
|
+
};
|