@aardworx/wombat.rendering 0.9.11 → 0.9.13
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/dist/runtime/derivedUniforms/codegen.d.ts +8 -0
- package/dist/runtime/derivedUniforms/codegen.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/codegen.js +449 -0
- package/dist/runtime/derivedUniforms/codegen.js.map +1 -0
- package/dist/runtime/derivedUniforms/dispatch.d.ts +14 -93
- package/dist/runtime/derivedUniforms/dispatch.d.ts.map +1 -1
- package/dist/runtime/derivedUniforms/dispatch.js +123 -299
- package/dist/runtime/derivedUniforms/dispatch.js.map +1 -1
- package/dist/runtime/derivedUniforms/flatten.d.ts +25 -0
- package/dist/runtime/derivedUniforms/flatten.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/flatten.js +71 -0
- package/dist/runtime/derivedUniforms/flatten.js.map +1 -0
- package/dist/runtime/derivedUniforms/index.d.ts +10 -5
- package/dist/runtime/derivedUniforms/index.d.ts.map +1 -1
- package/dist/runtime/derivedUniforms/index.js +24 -19
- package/dist/runtime/derivedUniforms/index.js.map +1 -1
- package/dist/runtime/derivedUniforms/marker.d.ts +76 -0
- package/dist/runtime/derivedUniforms/marker.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/marker.js +156 -0
- package/dist/runtime/derivedUniforms/marker.js.map +1 -0
- package/dist/runtime/derivedUniforms/recipes.d.ts +6 -41
- package/dist/runtime/derivedUniforms/recipes.d.ts.map +1 -1
- package/dist/runtime/derivedUniforms/recipes.js +53 -129
- package/dist/runtime/derivedUniforms/recipes.js.map +1 -1
- package/dist/runtime/derivedUniforms/records.d.ts +40 -0
- package/dist/runtime/derivedUniforms/records.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/records.js +151 -0
- package/dist/runtime/derivedUniforms/records.js.map +1 -0
- package/dist/runtime/derivedUniforms/registry.d.ts +33 -0
- package/dist/runtime/derivedUniforms/registry.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/registry.js +58 -0
- package/dist/runtime/derivedUniforms/registry.js.map +1 -0
- package/dist/runtime/derivedUniforms/rule.d.ts +26 -0
- package/dist/runtime/derivedUniforms/rule.d.ts.map +1 -0
- package/dist/runtime/derivedUniforms/rule.js +35 -0
- package/dist/runtime/derivedUniforms/rule.js.map +1 -0
- package/dist/runtime/derivedUniforms/sceneIntegration.d.ts +34 -33
- package/dist/runtime/derivedUniforms/sceneIntegration.d.ts.map +1 -1
- package/dist/runtime/derivedUniforms/sceneIntegration.js +80 -90
- package/dist/runtime/derivedUniforms/sceneIntegration.js.map +1 -1
- package/dist/runtime/heapScene.d.ts.map +1 -1
- package/dist/runtime/heapScene.js +78 -26
- package/dist/runtime/heapScene.js.map +1 -1
- package/dist/runtime/hybridScene.js +1 -1
- package/dist/runtime/hybridScene.js.map +1 -1
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/renderTask.js +1 -1
- package/dist/runtime/renderTask.js.map +1 -1
- package/dist/runtime/runtime.js +1 -1
- package/dist/runtime/runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/runtime/derivedUniforms/codegen.ts +477 -0
- package/src/runtime/derivedUniforms/dispatch.ts +135 -341
- package/src/runtime/derivedUniforms/flatten.ts +89 -0
- package/src/runtime/derivedUniforms/index.ts +50 -34
- package/src/runtime/derivedUniforms/marker.ts +169 -0
- package/src/runtime/derivedUniforms/recipes.ts +63 -155
- package/src/runtime/derivedUniforms/records.ts +158 -0
- package/src/runtime/derivedUniforms/registry.ts +77 -0
- package/src/runtime/derivedUniforms/rule.ts +59 -0
- package/src/runtime/derivedUniforms/sceneIntegration.ts +115 -149
- package/src/runtime/heapScene.ts +77 -28
- package/src/runtime/hybridScene.ts +1 -1
- package/src/runtime/index.ts +8 -0
- package/src/runtime/renderTask.ts +1 -1
- package/src/runtime/runtime.ts +1 -1
- package/dist/runtime/derivedUniforms/slotId.d.ts +0 -15
- package/dist/runtime/derivedUniforms/slotId.d.ts.map +0 -1
- package/dist/runtime/derivedUniforms/slotId.js +0 -30
- package/dist/runtime/derivedUniforms/slotId.js.map +0 -1
- package/dist/runtime/derivedUniforms/uberKernel.wgsl.d.ts +0 -13
- package/dist/runtime/derivedUniforms/uberKernel.wgsl.d.ts.map +0 -1
- package/dist/runtime/derivedUniforms/uberKernel.wgsl.js +0 -218
- package/dist/runtime/derivedUniforms/uberKernel.wgsl.js.map +0 -1
- package/src/runtime/derivedUniforms/slotId.ts +0 -35
- package/src/runtime/derivedUniforms/uberKernel.wgsl.ts +0 -220
|
@@ -225,7 +225,7 @@ export function compileHybridScene(
|
|
|
225
225
|
fragmentOutputLayout,
|
|
226
226
|
atlasPool,
|
|
227
227
|
...(opts.enableFamilyMerge === true ? { enableFamilyMerge: true } : {}),
|
|
228
|
-
...(opts.enableDerivedUniforms ===
|
|
228
|
+
...(opts.enableDerivedUniforms === false ? { enableDerivedUniforms: false } : {}),
|
|
229
229
|
});
|
|
230
230
|
|
|
231
231
|
// ─── Legacy subset → RenderTree → ScenePass ──────────────────────
|
package/src/runtime/index.ts
CHANGED
|
@@ -43,6 +43,14 @@ export { flattenRenderTree } from "./flattenTree.js";
|
|
|
43
43
|
export { isHeapEligible } from "./heapEligibility.js";
|
|
44
44
|
export { renderObjectToHeapSpec } from "./heapAdapter.js";
|
|
45
45
|
|
|
46
|
+
// ─── Derived uniforms (§7): a uniform binding is a value (aval/constant) OR a rule ──
|
|
47
|
+
export {
|
|
48
|
+
derivedUniform, DerivedExpr, type DerivedScope,
|
|
49
|
+
ruleFromIR, uniformRef, isDerivedRule, hashIR,
|
|
50
|
+
type DerivedRule, type IRFragment,
|
|
51
|
+
STANDARD_DERIVED_RULES, isStandardDerivedName,
|
|
52
|
+
} from "./derivedUniforms/index.js";
|
|
53
|
+
|
|
46
54
|
export {
|
|
47
55
|
compileHybridScene,
|
|
48
56
|
type CompileHybridSceneOptions,
|
|
@@ -255,7 +255,7 @@ class RenderTask implements IRenderTask {
|
|
|
255
255
|
compileEffect: this.ctx.compileEffect,
|
|
256
256
|
...(this.ctx.heapEnabled !== undefined ? { heapEnabled: this.ctx.heapEnabled } : {}),
|
|
257
257
|
...(this.ctx.enableFamilyMerge === true ? { enableFamilyMerge: true } : {}),
|
|
258
|
-
...(this.ctx.enableDerivedUniforms ===
|
|
258
|
+
...(this.ctx.enableDerivedUniforms === false ? { enableDerivedUniforms: false } : {}),
|
|
259
259
|
});
|
|
260
260
|
this._scenes.set(cmd, s);
|
|
261
261
|
}
|
package/src/runtime/runtime.ts
CHANGED
|
@@ -76,7 +76,7 @@ export class Runtime {
|
|
|
76
76
|
})),
|
|
77
77
|
...(opts.heapEnabled !== undefined ? { heapEnabled: opts.heapEnabled } : {}),
|
|
78
78
|
...(opts.enableFamilyMerge === true ? { enableFamilyMerge: true } : {}),
|
|
79
|
-
...(opts.enableDerivedUniforms ===
|
|
79
|
+
...(opts.enableDerivedUniforms === false ? { enableDerivedUniforms: false } : {}),
|
|
80
80
|
};
|
|
81
81
|
// `device.lost` is a real-WebGPU promise; mock devices may not
|
|
82
82
|
// expose it. Treat as "never lost" in that case.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** Branded u32 — top bit selects heap, low 31 bits are slot index. */
|
|
2
|
-
export type SlotId = number & {
|
|
3
|
-
readonly __slotId: unique symbol;
|
|
4
|
-
};
|
|
5
|
-
export declare const SlotId: {
|
|
6
|
-
/** Encode a Constituents-heap slot. */
|
|
7
|
-
constituent(idx: number): SlotId;
|
|
8
|
-
/** Encode an Intermediates-heap slot. */
|
|
9
|
-
intermediate(idx: number): SlotId;
|
|
10
|
-
isIntermediate(s: SlotId): boolean;
|
|
11
|
-
index(s: SlotId): number;
|
|
12
|
-
};
|
|
13
|
-
/** Bytes per df32 mat4 slot: 16 entries × 2 floats × 4 bytes. */
|
|
14
|
-
export declare const DF32_MAT4_BYTES = 128;
|
|
15
|
-
//# sourceMappingURL=slotId.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slotId.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/slotId.ts"],"names":[],"mappings":"AAWA,sEAAsE;AACtE,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,MAAM,CAAA;CAAE,CAAC;AAInE,eAAO,MAAM,MAAM;IACjB,uCAAuC;qBACtB,MAAM,GAAG,MAAM;IAGhC,yCAAyC;sBACvB,MAAM,GAAG,MAAM;sBAGf,MAAM,GAAG,OAAO;aAGzB,MAAM,GAAG,MAAM;CAGzB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,eAAe,MAAM,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Slot-ID encoding for the §7 uber kernel.
|
|
2
|
-
//
|
|
3
|
-
// One u32 namespace covers both the Constituents heap (df32 trafos
|
|
4
|
-
// uploaded from CPU) and the Intermediates heap (df32 outputs of
|
|
5
|
-
// layer-1 recipes). The high bit selects the heap; the low 31 bits are
|
|
6
|
-
// the linear index within that heap. Each slot holds one mat4 in df32
|
|
7
|
-
// = 16 vec2<f32> = 128 bytes.
|
|
8
|
-
//
|
|
9
|
-
// Uber-kernel side: a single helper reads from either heap based on
|
|
10
|
-
// the flag — see `read_mat4_df` in the WGSL source.
|
|
11
|
-
const FLAG_INTERMEDIATE = 0x80000000;
|
|
12
|
-
export const SlotId = {
|
|
13
|
-
/** Encode a Constituents-heap slot. */
|
|
14
|
-
constituent(idx) {
|
|
15
|
-
return (idx & 0x7FFFFFFF);
|
|
16
|
-
},
|
|
17
|
-
/** Encode an Intermediates-heap slot. */
|
|
18
|
-
intermediate(idx) {
|
|
19
|
-
return ((idx & 0x7FFFFFFF) | FLAG_INTERMEDIATE) >>> 0;
|
|
20
|
-
},
|
|
21
|
-
isIntermediate(s) {
|
|
22
|
-
return (s & FLAG_INTERMEDIATE) !== 0;
|
|
23
|
-
},
|
|
24
|
-
index(s) {
|
|
25
|
-
return s & 0x7FFFFFFF;
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
/** Bytes per df32 mat4 slot: 16 entries × 2 floats × 4 bytes. */
|
|
29
|
-
export const DF32_MAT4_BYTES = 128;
|
|
30
|
-
//# sourceMappingURL=slotId.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slotId.js","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/slotId.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,mEAAmE;AACnE,iEAAiE;AACjE,uEAAuE;AACvE,sEAAsE;AACtE,8BAA8B;AAC9B,EAAE;AACF,oEAAoE;AACpE,oDAAoD;AAKpD,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAErC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,uCAAuC;IACvC,WAAW,CAAC,GAAW;QACrB,OAAO,CAAC,GAAG,GAAG,UAAU,CAAW,CAAC;IACtC,CAAC;IACD,yCAAyC;IACzC,YAAY,CAAC,GAAW;QACtB,OAAO,CAAC,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAW,CAAC;IAClE,CAAC;IACD,cAAc,CAAC,CAAS;QACtB,OAAO,CAAC,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IACD,KAAK,CAAC,CAAS;QACb,OAAO,CAAC,GAAG,UAAU,CAAC;IACxB,CAAC;CACF,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare const DERIVED_UBER_KERNEL_WGSL = "\n\n// \u2500\u2500\u2500 Bindings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n@group(0) @binding(0) var<storage, read> Constituents: array<vec2<f32>>;\n@group(0) @binding(1) var<storage, read_write> MainHeap: array<f32>;\n\nstruct Record {\n recipe_id: u32,\n in0: u32, // constituent slot index\n in1: u32, // constituent slot index, 0 if unused\n in2: u32, // constituent slot index, 0 if unused\n out_byte: u32, // byte offset in MainHeap\n _pad: u32, // bumps stride to 24 (defensively 16-aligned)\n}\n@group(0) @binding(2) var<storage, read> Records: array<Record>;\n\n// Live record count. arrayLength(Records) reports the GPU buffer's\n// capacity (rounded up by allocator growth), not the live count \u2014\n// trailing slots hold stale data from swap-removed records. Threads\n// past Count.count would read stale records and trample random\n// MainHeap bytes.\nstruct CountUniform { count: u32 }\n@group(0) @binding(3) var<uniform> Count: CountUniform;\n\n// \u2500\u2500\u2500 df32 primitives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfn split12(a: f32) -> vec2<f32> {\n let hi = bitcast<f32>(bitcast<u32>(a) & 0xFFFFE000u);\n let lo = a - hi;\n return vec2<f32>(hi, lo);\n}\n\nfn two_sum(a: f32, b: f32) -> vec2<f32> {\n let s = a + b;\n let bb = fma(1.0, s, -a);\n let t1 = fma(1.0, s, -bb);\n let t2 = fma(1.0, a, -t1);\n let t3 = fma(1.0, b, -bb);\n return vec2<f32>(s, t2 + t3);\n}\n\nfn quick_two_sum(a: f32, b: f32) -> vec2<f32> {\n let s = a + b;\n let t = fma(1.0, s, -a);\n return vec2<f32>(s, fma(1.0, b, -t));\n}\n\nfn two_prod(a: f32, b: f32) -> vec2<f32> {\n let p = a * b;\n let A = split12(a);\n let B = split12(b);\n let err = ((A.x * B.x - p) + A.x * B.y + A.y * B.x) + A.y * B.y;\n return vec2<f32>(p, err);\n}\n\nfn df_add(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {\n let s = two_sum(a.x, b.x);\n let t = two_sum(a.y, b.y);\n let s3 = quick_two_sum(s.x, s.y + t.x);\n return quick_two_sum(s3.x, s3.y + t.y);\n}\n\nfn df_mul(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {\n let p = two_prod(a.x, b.x);\n let cross1 = fma(a.x, b.y, p.y);\n let cross = fma(a.y, b.x, cross1);\n return quick_two_sum(p.x, cross);\n}\n\n// \u2500\u2500\u2500 Constituents access \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// One slot = 16 vec2<f32> = one df32 mat4 in row-major order\n// (matching Aardvark M44d convention). Element (row, col) of slot k\n// lives at vec2 index k*16 + row*4 + col.\n\nfn read_entry(slot: u32, row: u32, col: u32) -> vec2<f32> {\n return Constituents[slot * 16u + row * 4u + col];\n}\n\n// (A \u00B7 B) entry (r, c) in df32, both A and B in constituents.\nfn df_mul_entry(a_slot: u32, b_slot: u32, r: u32, c: u32) -> vec2<f32> {\n var acc = vec2<f32>(0.0, 0.0);\n for (var k: u32 = 0u; k < 4u; k = k + 1u) {\n acc = df_add(acc, df_mul(read_entry(a_slot, r, k), read_entry(b_slot, k, c)));\n }\n return acc;\n}\n\n// Compute one full row of (A \u00B7 B), returns 4 df32 entries.\nfn df_mul_row(a_slot: u32, b_slot: u32, r: u32) -> array<vec2<f32>, 4> {\n var out: array<vec2<f32>, 4>;\n for (var c: u32 = 0u; c < 4u; c = c + 1u) {\n out[c] = df_mul_entry(a_slot, b_slot, r, c);\n }\n return out;\n}\n\n// Triple product (A \u00B7 B \u00B7 C) entry (r, c). Uses 4 df_muls per entry\n// \u2014 recomputes the inner row each call. Acceptable: at most one\n// such recipe per RO and only on dirty frames.\nfn df_mul3_entry(a_slot: u32, b_slot: u32, c_slot: u32, r: u32, c: u32) -> vec2<f32> {\n // (A\u00B7B\u00B7C)[r,c] = sum_k (A\u00B7B)[r,k] \u00B7 C[k,c]\n var acc = vec2<f32>(0.0, 0.0);\n for (var k: u32 = 0u; k < 4u; k = k + 1u) {\n let ab_rk = df_mul_entry(a_slot, b_slot, r, k);\n acc = df_add(acc, df_mul(ab_rk, read_entry(c_slot, k, c)));\n }\n return acc;\n}\n\n// \u2500\u2500\u2500 Writers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Mat4: 16 contiguous f32s, row-major.\n// Mat3: 3 rows \u00D7 4-stride (std140 mat3<f32>), 12 f32s; last column\n// per row left untouched.\n\nfn write_mat4_entry(out_byte: u32, row: u32, col: u32, val: f32) {\n MainHeap[(out_byte >> 2u) + row * 4u + col] = val;\n}\n\nfn write_mat3_entry(out_byte: u32, row: u32, col: u32, val: f32) {\n MainHeap[(out_byte >> 2u) + row * 4u + col] = val;\n}\n\n// \u2500\u2500\u2500 Recipe arms \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfn run_collapse_mat4(in0: u32, out_byte: u32) {\n for (var r: u32 = 0u; r < 4u; r = r + 1u) {\n for (var c: u32 = 0u; c < 4u; c = c + 1u) {\n let e = read_entry(in0, r, c);\n write_mat4_entry(out_byte, r, c, e.x + e.y);\n }\n }\n}\n\n// NormalMatrix: (M\u207B\u00B9)\u1D40 upper-3\u00D73.\n// NM[i, j] = MInv[j, i]\nfn run_collapse_mat3_normal(in0: u32, out_byte: u32) {\n for (var i: u32 = 0u; i < 3u; i = i + 1u) {\n for (var j: u32 = 0u; j < 3u; j = j + 1u) {\n let e = read_entry(in0, j, i);\n write_mat3_entry(out_byte, i, j, e.x + e.y);\n }\n }\n}\n\nfn run_dfmul2_collapse(in0: u32, in1: u32, out_byte: u32) {\n for (var r: u32 = 0u; r < 4u; r = r + 1u) {\n for (var c: u32 = 0u; c < 4u; c = c + 1u) {\n let e = df_mul_entry(in0, in1, r, c);\n write_mat4_entry(out_byte, r, c, e.x + e.y);\n }\n }\n}\n\nfn run_dfmul3_collapse(in0: u32, in1: u32, in2: u32, out_byte: u32) {\n for (var r: u32 = 0u; r < 4u; r = r + 1u) {\n for (var c: u32 = 0u; c < 4u; c = c + 1u) {\n let e = df_mul3_entry(in0, in1, in2, r, c);\n write_mat4_entry(out_byte, r, c, e.x + e.y);\n }\n }\n}\n\n// \u2500\u2500\u2500 Entry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n if (gid.x >= Count.count) { return; }\n let r = Records[gid.x];\n switch r.recipe_id {\n case 0u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafo\n case 1u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafoInv\n case 2u: { run_collapse_mat3_normal(r.in0, r.out_byte); } // NormalMatrix\n case 3u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafo\n case 4u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafoInv\n case 5u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafo\n case 6u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafoInv\n case 7u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafo\n case 8u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafoInv\n case 9u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafo\n case 10u:{ run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafoInv\n case 11u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafo\n case 12u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafoInv\n default: { return; }\n }\n}\n\n";
|
|
2
|
-
/** Records are 5× u32 = 20 bytes. WGSL storage struct alignment of
|
|
3
|
-
* 4 means the array stride is 20 bytes. */
|
|
4
|
-
export declare const RECORD_STRIDE_BYTES = 24;
|
|
5
|
-
export declare const RECORD_STRIDE_U32 = 6;
|
|
6
|
-
export declare const RECORD_FIELD: {
|
|
7
|
-
readonly recipe_id: 0;
|
|
8
|
-
readonly in0: 1;
|
|
9
|
-
readonly in1: 2;
|
|
10
|
-
readonly in2: 3;
|
|
11
|
-
readonly out_byte: 4;
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=uberKernel.wgsl.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uberKernel.wgsl.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/uberKernel.wgsl.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,wBAAwB,y0QA4LpC,CAAC;AAEF;4CAC4C;AAC5C,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC"}
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
// §7 derived-uniforms uber kernel — single flat dispatch over leaves.
|
|
2
|
-
//
|
|
3
|
-
// Records are static-ish (only edited on RO add/remove). Each thread
|
|
4
|
-
// processes one record:
|
|
5
|
-
// 1. Load record (recipe_id, in0, in1, in2, out_byte).
|
|
6
|
-
// 2. Switch on recipe_id, compute inline (df_mul + collapse), write
|
|
7
|
-
// f32 mat4/mat3 to MainHeap at out_byte.
|
|
8
|
-
//
|
|
9
|
-
// No dirty test on the kernel: when constituents don't change, the
|
|
10
|
-
// kernel rewrites identical bits. Saves a CPU writeBuffer + a binding
|
|
11
|
-
// per dispatch. GPU does ~64 FLOPs per record × N records — trivial at
|
|
12
|
-
// the scales this runs at.
|
|
13
|
-
//
|
|
14
|
-
// df32 primitives mirror examples/df32-precision/. Two opacity hooks:
|
|
15
|
-
// - bitcast<f32>(bits & MASK) for Veltkamp split (integer ops, no
|
|
16
|
-
// algebraic identity).
|
|
17
|
-
// - fma(1.0, x, -y) for compensated subtractions.
|
|
18
|
-
export const DERIVED_UBER_KERNEL_WGSL = /* wgsl */ `
|
|
19
|
-
|
|
20
|
-
// ─── Bindings ─────────────────────────────────────────────────────
|
|
21
|
-
@group(0) @binding(0) var<storage, read> Constituents: array<vec2<f32>>;
|
|
22
|
-
@group(0) @binding(1) var<storage, read_write> MainHeap: array<f32>;
|
|
23
|
-
|
|
24
|
-
struct Record {
|
|
25
|
-
recipe_id: u32,
|
|
26
|
-
in0: u32, // constituent slot index
|
|
27
|
-
in1: u32, // constituent slot index, 0 if unused
|
|
28
|
-
in2: u32, // constituent slot index, 0 if unused
|
|
29
|
-
out_byte: u32, // byte offset in MainHeap
|
|
30
|
-
_pad: u32, // bumps stride to 24 (defensively 16-aligned)
|
|
31
|
-
}
|
|
32
|
-
@group(0) @binding(2) var<storage, read> Records: array<Record>;
|
|
33
|
-
|
|
34
|
-
// Live record count. arrayLength(Records) reports the GPU buffer's
|
|
35
|
-
// capacity (rounded up by allocator growth), not the live count —
|
|
36
|
-
// trailing slots hold stale data from swap-removed records. Threads
|
|
37
|
-
// past Count.count would read stale records and trample random
|
|
38
|
-
// MainHeap bytes.
|
|
39
|
-
struct CountUniform { count: u32 }
|
|
40
|
-
@group(0) @binding(3) var<uniform> Count: CountUniform;
|
|
41
|
-
|
|
42
|
-
// ─── df32 primitives ──────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
fn split12(a: f32) -> vec2<f32> {
|
|
45
|
-
let hi = bitcast<f32>(bitcast<u32>(a) & 0xFFFFE000u);
|
|
46
|
-
let lo = a - hi;
|
|
47
|
-
return vec2<f32>(hi, lo);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
fn two_sum(a: f32, b: f32) -> vec2<f32> {
|
|
51
|
-
let s = a + b;
|
|
52
|
-
let bb = fma(1.0, s, -a);
|
|
53
|
-
let t1 = fma(1.0, s, -bb);
|
|
54
|
-
let t2 = fma(1.0, a, -t1);
|
|
55
|
-
let t3 = fma(1.0, b, -bb);
|
|
56
|
-
return vec2<f32>(s, t2 + t3);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
fn quick_two_sum(a: f32, b: f32) -> vec2<f32> {
|
|
60
|
-
let s = a + b;
|
|
61
|
-
let t = fma(1.0, s, -a);
|
|
62
|
-
return vec2<f32>(s, fma(1.0, b, -t));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fn two_prod(a: f32, b: f32) -> vec2<f32> {
|
|
66
|
-
let p = a * b;
|
|
67
|
-
let A = split12(a);
|
|
68
|
-
let B = split12(b);
|
|
69
|
-
let err = ((A.x * B.x - p) + A.x * B.y + A.y * B.x) + A.y * B.y;
|
|
70
|
-
return vec2<f32>(p, err);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
fn df_add(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {
|
|
74
|
-
let s = two_sum(a.x, b.x);
|
|
75
|
-
let t = two_sum(a.y, b.y);
|
|
76
|
-
let s3 = quick_two_sum(s.x, s.y + t.x);
|
|
77
|
-
return quick_two_sum(s3.x, s3.y + t.y);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
fn df_mul(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {
|
|
81
|
-
let p = two_prod(a.x, b.x);
|
|
82
|
-
let cross1 = fma(a.x, b.y, p.y);
|
|
83
|
-
let cross = fma(a.y, b.x, cross1);
|
|
84
|
-
return quick_two_sum(p.x, cross);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ─── Constituents access ──────────────────────────────────────────
|
|
88
|
-
//
|
|
89
|
-
// One slot = 16 vec2<f32> = one df32 mat4 in row-major order
|
|
90
|
-
// (matching Aardvark M44d convention). Element (row, col) of slot k
|
|
91
|
-
// lives at vec2 index k*16 + row*4 + col.
|
|
92
|
-
|
|
93
|
-
fn read_entry(slot: u32, row: u32, col: u32) -> vec2<f32> {
|
|
94
|
-
return Constituents[slot * 16u + row * 4u + col];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// (A · B) entry (r, c) in df32, both A and B in constituents.
|
|
98
|
-
fn df_mul_entry(a_slot: u32, b_slot: u32, r: u32, c: u32) -> vec2<f32> {
|
|
99
|
-
var acc = vec2<f32>(0.0, 0.0);
|
|
100
|
-
for (var k: u32 = 0u; k < 4u; k = k + 1u) {
|
|
101
|
-
acc = df_add(acc, df_mul(read_entry(a_slot, r, k), read_entry(b_slot, k, c)));
|
|
102
|
-
}
|
|
103
|
-
return acc;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Compute one full row of (A · B), returns 4 df32 entries.
|
|
107
|
-
fn df_mul_row(a_slot: u32, b_slot: u32, r: u32) -> array<vec2<f32>, 4> {
|
|
108
|
-
var out: array<vec2<f32>, 4>;
|
|
109
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
110
|
-
out[c] = df_mul_entry(a_slot, b_slot, r, c);
|
|
111
|
-
}
|
|
112
|
-
return out;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Triple product (A · B · C) entry (r, c). Uses 4 df_muls per entry
|
|
116
|
-
// — recomputes the inner row each call. Acceptable: at most one
|
|
117
|
-
// such recipe per RO and only on dirty frames.
|
|
118
|
-
fn df_mul3_entry(a_slot: u32, b_slot: u32, c_slot: u32, r: u32, c: u32) -> vec2<f32> {
|
|
119
|
-
// (A·B·C)[r,c] = sum_k (A·B)[r,k] · C[k,c]
|
|
120
|
-
var acc = vec2<f32>(0.0, 0.0);
|
|
121
|
-
for (var k: u32 = 0u; k < 4u; k = k + 1u) {
|
|
122
|
-
let ab_rk = df_mul_entry(a_slot, b_slot, r, k);
|
|
123
|
-
acc = df_add(acc, df_mul(ab_rk, read_entry(c_slot, k, c)));
|
|
124
|
-
}
|
|
125
|
-
return acc;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ─── Writers ──────────────────────────────────────────────────────
|
|
129
|
-
//
|
|
130
|
-
// Mat4: 16 contiguous f32s, row-major.
|
|
131
|
-
// Mat3: 3 rows × 4-stride (std140 mat3<f32>), 12 f32s; last column
|
|
132
|
-
// per row left untouched.
|
|
133
|
-
|
|
134
|
-
fn write_mat4_entry(out_byte: u32, row: u32, col: u32, val: f32) {
|
|
135
|
-
MainHeap[(out_byte >> 2u) + row * 4u + col] = val;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
fn write_mat3_entry(out_byte: u32, row: u32, col: u32, val: f32) {
|
|
139
|
-
MainHeap[(out_byte >> 2u) + row * 4u + col] = val;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ─── Recipe arms ──────────────────────────────────────────────────
|
|
143
|
-
|
|
144
|
-
fn run_collapse_mat4(in0: u32, out_byte: u32) {
|
|
145
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
146
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
147
|
-
let e = read_entry(in0, r, c);
|
|
148
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// NormalMatrix: (M⁻¹)ᵀ upper-3×3.
|
|
154
|
-
// NM[i, j] = MInv[j, i]
|
|
155
|
-
fn run_collapse_mat3_normal(in0: u32, out_byte: u32) {
|
|
156
|
-
for (var i: u32 = 0u; i < 3u; i = i + 1u) {
|
|
157
|
-
for (var j: u32 = 0u; j < 3u; j = j + 1u) {
|
|
158
|
-
let e = read_entry(in0, j, i);
|
|
159
|
-
write_mat3_entry(out_byte, i, j, e.x + e.y);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
fn run_dfmul2_collapse(in0: u32, in1: u32, out_byte: u32) {
|
|
165
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
166
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
167
|
-
let e = df_mul_entry(in0, in1, r, c);
|
|
168
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
fn run_dfmul3_collapse(in0: u32, in1: u32, in2: u32, out_byte: u32) {
|
|
174
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
175
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
176
|
-
let e = df_mul3_entry(in0, in1, in2, r, c);
|
|
177
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ─── Entry ────────────────────────────────────────────────────────
|
|
183
|
-
|
|
184
|
-
@compute @workgroup_size(64)
|
|
185
|
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
186
|
-
if (gid.x >= Count.count) { return; }
|
|
187
|
-
let r = Records[gid.x];
|
|
188
|
-
switch r.recipe_id {
|
|
189
|
-
case 0u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafo
|
|
190
|
-
case 1u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafoInv
|
|
191
|
-
case 2u: { run_collapse_mat3_normal(r.in0, r.out_byte); } // NormalMatrix
|
|
192
|
-
case 3u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafo
|
|
193
|
-
case 4u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafoInv
|
|
194
|
-
case 5u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafo
|
|
195
|
-
case 6u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafoInv
|
|
196
|
-
case 7u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafo
|
|
197
|
-
case 8u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafoInv
|
|
198
|
-
case 9u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafo
|
|
199
|
-
case 10u:{ run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafoInv
|
|
200
|
-
case 11u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafo
|
|
201
|
-
case 12u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafoInv
|
|
202
|
-
default: { return; }
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
`;
|
|
207
|
-
/** Records are 5× u32 = 20 bytes. WGSL storage struct alignment of
|
|
208
|
-
* 4 means the array stride is 20 bytes. */
|
|
209
|
-
export const RECORD_STRIDE_BYTES = 24;
|
|
210
|
-
export const RECORD_STRIDE_U32 = 6;
|
|
211
|
-
export const RECORD_FIELD = {
|
|
212
|
-
recipe_id: 0,
|
|
213
|
-
in0: 1,
|
|
214
|
-
in1: 2,
|
|
215
|
-
in2: 3,
|
|
216
|
-
out_byte: 4,
|
|
217
|
-
};
|
|
218
|
-
//# sourceMappingURL=uberKernel.wgsl.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uberKernel.wgsl.js","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/uberKernel.wgsl.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,qEAAqE;AACrE,wBAAwB;AACxB,yDAAyD;AACzD,sEAAsE;AACtE,8CAA8C;AAC9C,EAAE;AACF,mEAAmE;AACnE,sEAAsE;AACtE,uEAAuE;AACvE,2BAA2B;AAC3B,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2BAA2B;AAC3B,oDAAoD;AAEpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4LlD,CAAC;AAEF;4CAC4C;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AACtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,SAAS,EAAE,CAAC;IACZ,GAAG,EAAQ,CAAC;IACZ,GAAG,EAAQ,CAAC;IACZ,GAAG,EAAQ,CAAC;IACZ,QAAQ,EAAG,CAAC;CACJ,CAAC"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// Slot-ID encoding for the §7 uber kernel.
|
|
2
|
-
//
|
|
3
|
-
// One u32 namespace covers both the Constituents heap (df32 trafos
|
|
4
|
-
// uploaded from CPU) and the Intermediates heap (df32 outputs of
|
|
5
|
-
// layer-1 recipes). The high bit selects the heap; the low 31 bits are
|
|
6
|
-
// the linear index within that heap. Each slot holds one mat4 in df32
|
|
7
|
-
// = 16 vec2<f32> = 128 bytes.
|
|
8
|
-
//
|
|
9
|
-
// Uber-kernel side: a single helper reads from either heap based on
|
|
10
|
-
// the flag — see `read_mat4_df` in the WGSL source.
|
|
11
|
-
|
|
12
|
-
/** Branded u32 — top bit selects heap, low 31 bits are slot index. */
|
|
13
|
-
export type SlotId = number & { readonly __slotId: unique symbol };
|
|
14
|
-
|
|
15
|
-
const FLAG_INTERMEDIATE = 0x80000000;
|
|
16
|
-
|
|
17
|
-
export const SlotId = {
|
|
18
|
-
/** Encode a Constituents-heap slot. */
|
|
19
|
-
constituent(idx: number): SlotId {
|
|
20
|
-
return (idx & 0x7FFFFFFF) as SlotId;
|
|
21
|
-
},
|
|
22
|
-
/** Encode an Intermediates-heap slot. */
|
|
23
|
-
intermediate(idx: number): SlotId {
|
|
24
|
-
return ((idx & 0x7FFFFFFF) | FLAG_INTERMEDIATE) >>> 0 as SlotId;
|
|
25
|
-
},
|
|
26
|
-
isIntermediate(s: SlotId): boolean {
|
|
27
|
-
return (s & FLAG_INTERMEDIATE) !== 0;
|
|
28
|
-
},
|
|
29
|
-
index(s: SlotId): number {
|
|
30
|
-
return s & 0x7FFFFFFF;
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/** Bytes per df32 mat4 slot: 16 entries × 2 floats × 4 bytes. */
|
|
35
|
-
export const DF32_MAT4_BYTES = 128;
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
// §7 derived-uniforms uber kernel — single flat dispatch over leaves.
|
|
2
|
-
//
|
|
3
|
-
// Records are static-ish (only edited on RO add/remove). Each thread
|
|
4
|
-
// processes one record:
|
|
5
|
-
// 1. Load record (recipe_id, in0, in1, in2, out_byte).
|
|
6
|
-
// 2. Switch on recipe_id, compute inline (df_mul + collapse), write
|
|
7
|
-
// f32 mat4/mat3 to MainHeap at out_byte.
|
|
8
|
-
//
|
|
9
|
-
// No dirty test on the kernel: when constituents don't change, the
|
|
10
|
-
// kernel rewrites identical bits. Saves a CPU writeBuffer + a binding
|
|
11
|
-
// per dispatch. GPU does ~64 FLOPs per record × N records — trivial at
|
|
12
|
-
// the scales this runs at.
|
|
13
|
-
//
|
|
14
|
-
// df32 primitives mirror examples/df32-precision/. Two opacity hooks:
|
|
15
|
-
// - bitcast<f32>(bits & MASK) for Veltkamp split (integer ops, no
|
|
16
|
-
// algebraic identity).
|
|
17
|
-
// - fma(1.0, x, -y) for compensated subtractions.
|
|
18
|
-
|
|
19
|
-
export const DERIVED_UBER_KERNEL_WGSL = /* wgsl */ `
|
|
20
|
-
|
|
21
|
-
// ─── Bindings ─────────────────────────────────────────────────────
|
|
22
|
-
@group(0) @binding(0) var<storage, read> Constituents: array<vec2<f32>>;
|
|
23
|
-
@group(0) @binding(1) var<storage, read_write> MainHeap: array<f32>;
|
|
24
|
-
|
|
25
|
-
struct Record {
|
|
26
|
-
recipe_id: u32,
|
|
27
|
-
in0: u32, // constituent slot index
|
|
28
|
-
in1: u32, // constituent slot index, 0 if unused
|
|
29
|
-
in2: u32, // constituent slot index, 0 if unused
|
|
30
|
-
out_byte: u32, // byte offset in MainHeap
|
|
31
|
-
_pad: u32, // bumps stride to 24 (defensively 16-aligned)
|
|
32
|
-
}
|
|
33
|
-
@group(0) @binding(2) var<storage, read> Records: array<Record>;
|
|
34
|
-
|
|
35
|
-
// Live record count. arrayLength(Records) reports the GPU buffer's
|
|
36
|
-
// capacity (rounded up by allocator growth), not the live count —
|
|
37
|
-
// trailing slots hold stale data from swap-removed records. Threads
|
|
38
|
-
// past Count.count would read stale records and trample random
|
|
39
|
-
// MainHeap bytes.
|
|
40
|
-
struct CountUniform { count: u32 }
|
|
41
|
-
@group(0) @binding(3) var<uniform> Count: CountUniform;
|
|
42
|
-
|
|
43
|
-
// ─── df32 primitives ──────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
fn split12(a: f32) -> vec2<f32> {
|
|
46
|
-
let hi = bitcast<f32>(bitcast<u32>(a) & 0xFFFFE000u);
|
|
47
|
-
let lo = a - hi;
|
|
48
|
-
return vec2<f32>(hi, lo);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
fn two_sum(a: f32, b: f32) -> vec2<f32> {
|
|
52
|
-
let s = a + b;
|
|
53
|
-
let bb = fma(1.0, s, -a);
|
|
54
|
-
let t1 = fma(1.0, s, -bb);
|
|
55
|
-
let t2 = fma(1.0, a, -t1);
|
|
56
|
-
let t3 = fma(1.0, b, -bb);
|
|
57
|
-
return vec2<f32>(s, t2 + t3);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fn quick_two_sum(a: f32, b: f32) -> vec2<f32> {
|
|
61
|
-
let s = a + b;
|
|
62
|
-
let t = fma(1.0, s, -a);
|
|
63
|
-
return vec2<f32>(s, fma(1.0, b, -t));
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
fn two_prod(a: f32, b: f32) -> vec2<f32> {
|
|
67
|
-
let p = a * b;
|
|
68
|
-
let A = split12(a);
|
|
69
|
-
let B = split12(b);
|
|
70
|
-
let err = ((A.x * B.x - p) + A.x * B.y + A.y * B.x) + A.y * B.y;
|
|
71
|
-
return vec2<f32>(p, err);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
fn df_add(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {
|
|
75
|
-
let s = two_sum(a.x, b.x);
|
|
76
|
-
let t = two_sum(a.y, b.y);
|
|
77
|
-
let s3 = quick_two_sum(s.x, s.y + t.x);
|
|
78
|
-
return quick_two_sum(s3.x, s3.y + t.y);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
fn df_mul(a: vec2<f32>, b: vec2<f32>) -> vec2<f32> {
|
|
82
|
-
let p = two_prod(a.x, b.x);
|
|
83
|
-
let cross1 = fma(a.x, b.y, p.y);
|
|
84
|
-
let cross = fma(a.y, b.x, cross1);
|
|
85
|
-
return quick_two_sum(p.x, cross);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ─── Constituents access ──────────────────────────────────────────
|
|
89
|
-
//
|
|
90
|
-
// One slot = 16 vec2<f32> = one df32 mat4 in row-major order
|
|
91
|
-
// (matching Aardvark M44d convention). Element (row, col) of slot k
|
|
92
|
-
// lives at vec2 index k*16 + row*4 + col.
|
|
93
|
-
|
|
94
|
-
fn read_entry(slot: u32, row: u32, col: u32) -> vec2<f32> {
|
|
95
|
-
return Constituents[slot * 16u + row * 4u + col];
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// (A · B) entry (r, c) in df32, both A and B in constituents.
|
|
99
|
-
fn df_mul_entry(a_slot: u32, b_slot: u32, r: u32, c: u32) -> vec2<f32> {
|
|
100
|
-
var acc = vec2<f32>(0.0, 0.0);
|
|
101
|
-
for (var k: u32 = 0u; k < 4u; k = k + 1u) {
|
|
102
|
-
acc = df_add(acc, df_mul(read_entry(a_slot, r, k), read_entry(b_slot, k, c)));
|
|
103
|
-
}
|
|
104
|
-
return acc;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Compute one full row of (A · B), returns 4 df32 entries.
|
|
108
|
-
fn df_mul_row(a_slot: u32, b_slot: u32, r: u32) -> array<vec2<f32>, 4> {
|
|
109
|
-
var out: array<vec2<f32>, 4>;
|
|
110
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
111
|
-
out[c] = df_mul_entry(a_slot, b_slot, r, c);
|
|
112
|
-
}
|
|
113
|
-
return out;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Triple product (A · B · C) entry (r, c). Uses 4 df_muls per entry
|
|
117
|
-
// — recomputes the inner row each call. Acceptable: at most one
|
|
118
|
-
// such recipe per RO and only on dirty frames.
|
|
119
|
-
fn df_mul3_entry(a_slot: u32, b_slot: u32, c_slot: u32, r: u32, c: u32) -> vec2<f32> {
|
|
120
|
-
// (A·B·C)[r,c] = sum_k (A·B)[r,k] · C[k,c]
|
|
121
|
-
var acc = vec2<f32>(0.0, 0.0);
|
|
122
|
-
for (var k: u32 = 0u; k < 4u; k = k + 1u) {
|
|
123
|
-
let ab_rk = df_mul_entry(a_slot, b_slot, r, k);
|
|
124
|
-
acc = df_add(acc, df_mul(ab_rk, read_entry(c_slot, k, c)));
|
|
125
|
-
}
|
|
126
|
-
return acc;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ─── Writers ──────────────────────────────────────────────────────
|
|
130
|
-
//
|
|
131
|
-
// Mat4: 16 contiguous f32s, row-major.
|
|
132
|
-
// Mat3: 3 rows × 4-stride (std140 mat3<f32>), 12 f32s; last column
|
|
133
|
-
// per row left untouched.
|
|
134
|
-
|
|
135
|
-
fn write_mat4_entry(out_byte: u32, row: u32, col: u32, val: f32) {
|
|
136
|
-
MainHeap[(out_byte >> 2u) + row * 4u + col] = val;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
fn write_mat3_entry(out_byte: u32, row: u32, col: u32, val: f32) {
|
|
140
|
-
MainHeap[(out_byte >> 2u) + row * 4u + col] = val;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// ─── Recipe arms ──────────────────────────────────────────────────
|
|
144
|
-
|
|
145
|
-
fn run_collapse_mat4(in0: u32, out_byte: u32) {
|
|
146
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
147
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
148
|
-
let e = read_entry(in0, r, c);
|
|
149
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// NormalMatrix: (M⁻¹)ᵀ upper-3×3.
|
|
155
|
-
// NM[i, j] = MInv[j, i]
|
|
156
|
-
fn run_collapse_mat3_normal(in0: u32, out_byte: u32) {
|
|
157
|
-
for (var i: u32 = 0u; i < 3u; i = i + 1u) {
|
|
158
|
-
for (var j: u32 = 0u; j < 3u; j = j + 1u) {
|
|
159
|
-
let e = read_entry(in0, j, i);
|
|
160
|
-
write_mat3_entry(out_byte, i, j, e.x + e.y);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
fn run_dfmul2_collapse(in0: u32, in1: u32, out_byte: u32) {
|
|
166
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
167
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
168
|
-
let e = df_mul_entry(in0, in1, r, c);
|
|
169
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
fn run_dfmul3_collapse(in0: u32, in1: u32, in2: u32, out_byte: u32) {
|
|
175
|
-
for (var r: u32 = 0u; r < 4u; r = r + 1u) {
|
|
176
|
-
for (var c: u32 = 0u; c < 4u; c = c + 1u) {
|
|
177
|
-
let e = df_mul3_entry(in0, in1, in2, r, c);
|
|
178
|
-
write_mat4_entry(out_byte, r, c, e.x + e.y);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ─── Entry ────────────────────────────────────────────────────────
|
|
184
|
-
|
|
185
|
-
@compute @workgroup_size(64)
|
|
186
|
-
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
187
|
-
if (gid.x >= Count.count) { return; }
|
|
188
|
-
let r = Records[gid.x];
|
|
189
|
-
switch r.recipe_id {
|
|
190
|
-
case 0u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafo
|
|
191
|
-
case 1u: { run_collapse_mat4(r.in0, r.out_byte); } // ModelTrafoInv
|
|
192
|
-
case 2u: { run_collapse_mat3_normal(r.in0, r.out_byte); } // NormalMatrix
|
|
193
|
-
case 3u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafo
|
|
194
|
-
case 4u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ModelViewTrafoInv
|
|
195
|
-
case 5u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafo
|
|
196
|
-
case 6u: { run_dfmul3_collapse(r.in0, r.in1, r.in2, r.out_byte); } // ModelViewProjTrafoInv
|
|
197
|
-
case 7u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafo
|
|
198
|
-
case 8u: { run_collapse_mat4(r.in0, r.out_byte); } // ViewTrafoInv
|
|
199
|
-
case 9u: { run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafo
|
|
200
|
-
case 10u:{ run_dfmul2_collapse(r.in0, r.in1, r.out_byte); } // ViewProjTrafoInv
|
|
201
|
-
case 11u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafo
|
|
202
|
-
case 12u:{ run_collapse_mat4(r.in0, r.out_byte); } // ProjTrafoInv
|
|
203
|
-
default: { return; }
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
`;
|
|
208
|
-
|
|
209
|
-
/** Records are 5× u32 = 20 bytes. WGSL storage struct alignment of
|
|
210
|
-
* 4 means the array stride is 20 bytes. */
|
|
211
|
-
export const RECORD_STRIDE_BYTES = 24;
|
|
212
|
-
export const RECORD_STRIDE_U32 = 6;
|
|
213
|
-
|
|
214
|
-
export const RECORD_FIELD = {
|
|
215
|
-
recipe_id: 0,
|
|
216
|
-
in0: 1,
|
|
217
|
-
in1: 2,
|
|
218
|
-
in2: 3,
|
|
219
|
-
out_byte: 4,
|
|
220
|
-
} as const;
|