@aardworx/wombat.rendering 0.9.32 → 0.18.1

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.
Files changed (95) hide show
  1. package/dist/runtime/derivedModes/kernelCodegen.d.ts +90 -0
  2. package/dist/runtime/derivedModes/kernelCodegen.d.ts.map +1 -0
  3. package/dist/runtime/derivedModes/kernelCodegen.js +623 -0
  4. package/dist/runtime/derivedModes/kernelCodegen.js.map +1 -0
  5. package/dist/runtime/derivedModes/modeKeyCpu.d.ts.map +1 -1
  6. package/dist/runtime/derivedModes/modeKeyCpu.js +23 -67
  7. package/dist/runtime/derivedModes/modeKeyCpu.js.map +1 -1
  8. package/dist/runtime/derivedModes/partitionDispatcher.d.ts +79 -0
  9. package/dist/runtime/derivedModes/partitionDispatcher.d.ts.map +1 -0
  10. package/dist/runtime/derivedModes/partitionDispatcher.js +303 -0
  11. package/dist/runtime/derivedModes/partitionDispatcher.js.map +1 -0
  12. package/dist/runtime/derivedModes/partitionKernel.d.ts +12 -0
  13. package/dist/runtime/derivedModes/partitionKernel.d.ts.map +1 -0
  14. package/dist/runtime/derivedModes/partitionKernel.js +127 -0
  15. package/dist/runtime/derivedModes/partitionKernel.js.map +1 -0
  16. package/dist/runtime/derivedModes/partitionKernelLayout.d.ts +4 -0
  17. package/dist/runtime/derivedModes/partitionKernelLayout.d.ts.map +1 -0
  18. package/dist/runtime/derivedModes/partitionKernelLayout.js +24 -0
  19. package/dist/runtime/derivedModes/partitionKernelLayout.js.map +1 -0
  20. package/dist/runtime/derivedModes/rule.d.ts +38 -86
  21. package/dist/runtime/derivedModes/rule.d.ts.map +1 -1
  22. package/dist/runtime/derivedModes/rule.js +34 -74
  23. package/dist/runtime/derivedModes/rule.js.map +1 -1
  24. package/dist/runtime/derivedUniforms/dispatch.d.ts +10 -8
  25. package/dist/runtime/derivedUniforms/dispatch.d.ts.map +1 -1
  26. package/dist/runtime/derivedUniforms/dispatch.js +31 -18
  27. package/dist/runtime/derivedUniforms/dispatch.js.map +1 -1
  28. package/dist/runtime/derivedUniforms/marker.d.ts +90 -11
  29. package/dist/runtime/derivedUniforms/marker.d.ts.map +1 -1
  30. package/dist/runtime/derivedUniforms/marker.js +161 -15
  31. package/dist/runtime/derivedUniforms/marker.js.map +1 -1
  32. package/dist/runtime/derivedUniforms/sceneIntegration.d.ts +25 -6
  33. package/dist/runtime/derivedUniforms/sceneIntegration.d.ts.map +1 -1
  34. package/dist/runtime/derivedUniforms/sceneIntegration.js +61 -20
  35. package/dist/runtime/derivedUniforms/sceneIntegration.js.map +1 -1
  36. package/dist/runtime/heapEligibility.d.ts.map +1 -1
  37. package/dist/runtime/heapEligibility.js +22 -2
  38. package/dist/runtime/heapEligibility.js.map +1 -1
  39. package/dist/runtime/heapScene/chunkedArena.d.ts +76 -0
  40. package/dist/runtime/heapScene/chunkedArena.d.ts.map +1 -0
  41. package/dist/runtime/heapScene/chunkedArena.js +223 -0
  42. package/dist/runtime/heapScene/chunkedArena.js.map +1 -0
  43. package/dist/runtime/heapScene/freelist.d.ts +50 -0
  44. package/dist/runtime/heapScene/freelist.d.ts.map +1 -0
  45. package/dist/runtime/heapScene/freelist.js +160 -0
  46. package/dist/runtime/heapScene/freelist.js.map +1 -0
  47. package/dist/runtime/heapScene/growBuffer.d.ts +23 -2
  48. package/dist/runtime/heapScene/growBuffer.d.ts.map +1 -1
  49. package/dist/runtime/heapScene/growBuffer.js +35 -3
  50. package/dist/runtime/heapScene/growBuffer.js.map +1 -1
  51. package/dist/runtime/heapScene/pools.d.ts +65 -42
  52. package/dist/runtime/heapScene/pools.d.ts.map +1 -1
  53. package/dist/runtime/heapScene/pools.js +206 -134
  54. package/dist/runtime/heapScene/pools.js.map +1 -1
  55. package/dist/runtime/heapScene.d.ts +12 -0
  56. package/dist/runtime/heapScene.d.ts.map +1 -1
  57. package/dist/runtime/heapScene.js +949 -167
  58. package/dist/runtime/heapScene.js.map +1 -1
  59. package/dist/runtime/hybridScene.d.ts +7 -0
  60. package/dist/runtime/hybridScene.d.ts.map +1 -1
  61. package/dist/runtime/hybridScene.js +1 -0
  62. package/dist/runtime/hybridScene.js.map +1 -1
  63. package/dist/runtime/index.d.ts +4 -3
  64. package/dist/runtime/index.d.ts.map +1 -1
  65. package/dist/runtime/index.js +4 -3
  66. package/dist/runtime/index.js.map +1 -1
  67. package/dist/runtime/renderTask.d.ts +5 -0
  68. package/dist/runtime/renderTask.d.ts.map +1 -1
  69. package/dist/runtime/renderTask.js +1 -0
  70. package/dist/runtime/renderTask.js.map +1 -1
  71. package/dist/runtime/runtime.d.ts +6 -0
  72. package/dist/runtime/runtime.d.ts.map +1 -1
  73. package/dist/runtime/runtime.js +1 -0
  74. package/dist/runtime/runtime.js.map +1 -1
  75. package/package.json +1 -1
  76. package/src/runtime/derivedModes/kernelCodegen.ts +673 -0
  77. package/src/runtime/derivedModes/modeKeyCpu.ts +22 -60
  78. package/src/runtime/derivedModes/partitionDispatcher.ts +349 -0
  79. package/src/runtime/derivedModes/partitionKernelLayout.ts +25 -0
  80. package/src/runtime/derivedModes/rule.ts +66 -145
  81. package/src/runtime/derivedUniforms/dispatch.ts +48 -21
  82. package/src/runtime/derivedUniforms/marker.ts +210 -16
  83. package/src/runtime/derivedUniforms/sceneIntegration.ts +67 -20
  84. package/src/runtime/heapEligibility.ts +22 -1
  85. package/src/runtime/heapScene/chunkedArena.ts +238 -0
  86. package/src/runtime/heapScene/freelist.ts +161 -0
  87. package/src/runtime/heapScene/growBuffer.ts +39 -2
  88. package/src/runtime/heapScene/pools.ts +231 -143
  89. package/src/runtime/heapScene.ts +1134 -161
  90. package/src/runtime/hybridScene.ts +8 -0
  91. package/src/runtime/index.ts +12 -9
  92. package/src/runtime/renderTask.ts +6 -0
  93. package/src/runtime/runtime.ts +7 -0
  94. package/src/runtime/derivedModes/gpuDispatcher.ts +0 -226
  95. package/src/runtime/derivedModes/gpuKernel.ts +0 -98
@@ -0,0 +1,127 @@
1
+ // Phase 5c.3 partition kernel — GPU-driven record routing.
2
+ //
3
+ // Each frame the kernel runs over a bucket's master record pool. For
4
+ // each record, it evaluates the rule (currently hard-coded: flip
5
+ // cullMode by sign of det(upperLeft3x3) of the record's ModelTrafo),
6
+ // picks the destination slot index (0 = declared, 1 = flipped),
7
+ // atomically reserves a write offset in that slot, and copies the
8
+ // record into `slotDrawTable[slotIdx][offset]`.
9
+ //
10
+ // Buffer layout (one bucket):
11
+ //
12
+ // masterRecords: array<Record> // CPU populates at addRO
13
+ // numRecords: u32 (uniform)
14
+ // slot0Indirect: array<u32, 4> // (indexCount, instanceCount=1, 0, 0)
15
+ // slot1Indirect: array<u32, 4> // ditto
16
+ // slot0DrawTable: array<Record> // partition output
17
+ // slot1DrawTable: array<Record> // partition output
18
+ // slot0ScanParams: { numRecords: u32 ... }
19
+ // slot1ScanParams: { numRecords: u32 ... }
20
+ //
21
+ // The existing scan (HEAP_SCAN_WGSL in heapScene/scanKernel.ts) runs
22
+ // per slot AFTER partition: it reads the slot's drawTable, computes
23
+ // firstEmit prefix sums, and writes the final indirect[0] = totalEmit.
24
+ //
25
+ // What this kernel does NOT do:
26
+ // - Generalize beyond the determinant-flip-cull rule (v1 only).
27
+ // - Handle >2 slots per bucket. (For multi-output rules, future
28
+ // work: dynamic slot count, modeKey-table lookup.)
29
+ // - Manage record removal — that's CPU at removeDraw time (swap-pop
30
+ // the master pool).
31
+ import { CULL_TO_U32 } from "./gpuKernel.js";
32
+ /** Record layout: 6 × u32 = 24 bytes per record. Matches the heap
33
+ * scan's record format (5 × u32) plus a `modelRef` byte offset. */
34
+ export const PARTITION_RECORD_U32 = 6;
35
+ export const PARTITION_RECORD_BYTES = PARTITION_RECORD_U32 * 4;
36
+ /** Per-bucket partition kernel WGSL. Two output slots: declared
37
+ * + flipped. */
38
+ export const PARTITION_FLIP_CULL_BY_DET_WGSL = /* wgsl */ `
39
+ struct Record {
40
+ // firstEmit is GPU-written by the scan; leave as 0 here.
41
+ firstEmit: u32,
42
+ drawIdx: u32,
43
+ indexStart: u32,
44
+ indexCount: u32,
45
+ instanceCount: u32,
46
+ modelRef: u32,
47
+ };
48
+ struct PartitionParams {
49
+ numRecords: u32,
50
+ declaredCull: u32, // 0=none, 1=front, 2=back
51
+ _pad0: u32,
52
+ _pad1: u32,
53
+ };
54
+
55
+ @group(0) @binding(0) var<storage, read> arena: array<u32>;
56
+ @group(0) @binding(1) var<storage, read> masterRecords: array<Record>;
57
+ @group(0) @binding(2) var<uniform> params: PartitionParams;
58
+ // Slot 0 = declared cull mode. Slot 1 = flipped cull mode.
59
+ @group(0) @binding(3) var<storage, read_write> slot0Count: array<atomic<u32>>; // length 1
60
+ @group(0) @binding(4) var<storage, read_write> slot1Count: array<atomic<u32>>;
61
+ // Slot draw tables hold scan-format records (5 × u32 per record:
62
+ // firstEmit, drawIdx, indexStart, indexCount, instanceCount). We
63
+ // drop modelRef on scatter — only the partition kernel needs it,
64
+ // and including it would force the scan to read a 6-u32 stride.
65
+ @group(0) @binding(5) var<storage, read_write> slot0DrawTable: array<u32>;
66
+ @group(0) @binding(6) var<storage, read_write> slot1DrawTable: array<u32>;
67
+
68
+ const SCAN_REC_U32: u32 = 5u;
69
+
70
+ fn loadUpper3x3(refBytes: u32) -> array<f32, 9> {
71
+ let baseU32 = (refBytes + 16u) >> 2u;
72
+ return array<f32, 9>(
73
+ bitcast<f32>(arena[baseU32 + 0u]), bitcast<f32>(arena[baseU32 + 1u]), bitcast<f32>(arena[baseU32 + 2u]),
74
+ bitcast<f32>(arena[baseU32 + 4u]), bitcast<f32>(arena[baseU32 + 5u]), bitcast<f32>(arena[baseU32 + 6u]),
75
+ bitcast<f32>(arena[baseU32 + 8u]), bitcast<f32>(arena[baseU32 + 9u]), bitcast<f32>(arena[baseU32 + 10u]),
76
+ );
77
+ }
78
+
79
+ fn det3x3(m: array<f32, 9>) -> f32 {
80
+ return m[0] * (m[4] * m[8] - m[5] * m[7])
81
+ - m[1] * (m[3] * m[8] - m[5] * m[6])
82
+ + m[2] * (m[3] * m[7] - m[4] * m[6]);
83
+ }
84
+
85
+ @compute @workgroup_size(64)
86
+ fn clear(@builtin(global_invocation_id) gid: vec3<u32>) {
87
+ // One thread total clears both atomics. Cheap pass.
88
+ if (gid.x != 0u) { return; }
89
+ atomicStore(&slot0Count[0], 0u);
90
+ atomicStore(&slot1Count[0], 0u);
91
+ }
92
+
93
+ @compute @workgroup_size(64)
94
+ fn partition(@builtin(global_invocation_id) gid: vec3<u32>) {
95
+ let i = gid.x;
96
+ if (i >= params.numRecords) { return; }
97
+ let r = masterRecords[i];
98
+ let m = loadUpper3x3(r.modelRef);
99
+ let flipped = det3x3(m) < 0.0;
100
+ // slotIdx 0 = declared (not flipped), 1 = flipped.
101
+ // Atomic-add into the appropriate slot's count, then scatter the
102
+ // record into the slot's drawTable at the reserved offset.
103
+ if (flipped) {
104
+ let off = atomicAdd(&slot1Count[0], 1u);
105
+ let base = off * SCAN_REC_U32;
106
+ slot1DrawTable[base + 0u] = 0u; // firstEmit (scan rewrites)
107
+ slot1DrawTable[base + 1u] = r.drawIdx;
108
+ slot1DrawTable[base + 2u] = r.indexStart;
109
+ slot1DrawTable[base + 3u] = r.indexCount;
110
+ slot1DrawTable[base + 4u] = r.instanceCount;
111
+ } else {
112
+ let off = atomicAdd(&slot0Count[0], 1u);
113
+ let base = off * SCAN_REC_U32;
114
+ slot0DrawTable[base + 0u] = 0u;
115
+ slot0DrawTable[base + 1u] = r.drawIdx;
116
+ slot0DrawTable[base + 2u] = r.indexStart;
117
+ slot0DrawTable[base + 3u] = r.indexCount;
118
+ slot0DrawTable[base + 4u] = r.instanceCount;
119
+ }
120
+ }
121
+ `;
122
+ /** WGSL helper: pack a Trafo3d's upper-left 3×3 + a CullMode enum
123
+ * into the partition kernel's expected uniform layout. */
124
+ export function cullModeToU32(c) {
125
+ return CULL_TO_U32[c];
126
+ }
127
+ //# sourceMappingURL=partitionKernel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partitionKernel.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/partitionKernel.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,qEAAqE;AACrE,gEAAgE;AAChE,kEAAkE;AAClE,gDAAgD;AAChD,EAAE;AACF,8BAA8B;AAC9B,EAAE;AACF,sEAAsE;AACtE,iCAAiC;AACjC,mFAAmF;AACnF,qDAAqD;AACrD,gEAAgE;AAChE,gEAAgE;AAChE,6CAA6C;AAC7C,6CAA6C;AAC7C,EAAE;AACF,qEAAqE;AACrE,oEAAoE;AACpE,uEAAuE;AACvE,EAAE;AACF,gCAAgC;AAChC,kEAAkE;AAClE,kEAAkE;AAClE,uDAAuD;AACvD,sEAAsE;AACtE,wBAAwB;AAGxB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;oEACoE;AACpE,MAAM,CAAC,MAAM,oBAAoB,GAAK,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,GAAG,CAAC,CAAC;AAE/D;iBACiB;AACjB,MAAM,CAAC,MAAM,+BAA+B,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFzD,CAAC;AAEF;2DAC2D;AAC3D,MAAM,UAAU,aAAa,CAAC,CAAW;IACvC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const PARTITION_RECORD_PREFIX_U32 = 6;
2
+ export declare function partitionRecordU32(numUniforms: number): number;
3
+ export declare function partitionRecordBytes(numUniforms: number): number;
4
+ //# sourceMappingURL=partitionKernelLayout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partitionKernelLayout.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/partitionKernelLayout.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AACD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE"}
@@ -0,0 +1,24 @@
1
+ // Partition kernel record layout — shared by the codegen + dispatcher.
2
+ //
3
+ // Each record in the master buffer has a FIXED 6-u32 prefix and a
4
+ // per-bucket variable tail of K uniform refs:
5
+ //
6
+ // [firstEmit, drawIdx, indexStart, indexCount, instanceCount, comboId,
7
+ // ref0, ref1, …, ref{K-1}]
8
+ //
9
+ // `firstEmit` is reserved (GPU-written by the downstream scan); CPU
10
+ // writes 0. `comboId` selects which combo of axis-rules the partition
11
+ // kernel evaluates — a combo is a per-RO choice of (at most one) rule
12
+ // per mode axis. Each `refI` is the per-RO arena byte offset of the
13
+ // I-th uniform appearing across all rules in the bucket
14
+ // (`bucket.uniformOrder`). The kernel reads `r.refI` for whichever
15
+ // uniform a given rule body declares via `ReadInput("Uniform", name)`
16
+ // — the codegen translates name → refI lookup.
17
+ export const PARTITION_RECORD_PREFIX_U32 = 6;
18
+ export function partitionRecordU32(numUniforms) {
19
+ return PARTITION_RECORD_PREFIX_U32 + numUniforms;
20
+ }
21
+ export function partitionRecordBytes(numUniforms) {
22
+ return partitionRecordU32(numUniforms) * 4;
23
+ }
24
+ //# sourceMappingURL=partitionKernelLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partitionKernelLayout.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/partitionKernelLayout.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,kEAAkE;AAClE,8CAA8C;AAC9C,EAAE;AACF,yEAAyE;AACzE,8BAA8B;AAC9B,EAAE;AACF,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,oEAAoE;AACpE,wDAAwD;AACxD,mEAAmE;AACnE,sEAAsE;AACtE,+CAA+C;AAE/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAE7C,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,OAAO,2BAA2B,GAAG,WAAW,CAAC;AACnD,CAAC;AACD,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,OAAO,kBAAkB,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC"}
@@ -1,98 +1,50 @@
1
- import type { CullMode, FrontFace, Topology } from "../pipelineCache/index.js";
2
- /** Discrete pipeline-state axes a `derivedMode` rule can target. v1
3
- * supports cull / frontFace / topology / depthCompare / depthWrite /
4
- * blendEnabled. Stencil + per-attachment blend factor/operation are
5
- * deferred same carve-outs as `derived-modes.md`. */
6
- export type ModeAxis = "cull" | "frontFace" | "topology" | "depthCompare" | "depthWrite" | "alphaToCoverage";
7
- /** Output value type per axis. */
8
- export type ModeValue<A extends ModeAxis> = A extends "cull" ? CullMode : A extends "frontFace" ? FrontFace : A extends "topology" ? Topology : A extends "depthCompare" ? GPUCompareFunction : A extends "depthWrite" ? boolean : A extends "alphaToCoverage" ? boolean : never;
9
- /**
10
- * The closure signature a `derivedMode` rule body takes:
11
- * - `u`: a proxy whose properties resolve to the RO's uniform values
12
- * at evaluation time. The proxy is type-erased in v1 declare the
13
- * uniforms you'll read via `UniformScope` augmentation on the
14
- * wombat.shader side for compile-time safety, or annotate locally.
15
- * - `declared`: the SG-declared value for this axis at this leaf.
16
- * Identity rules `(u, d) => d` are valid and useful for axes that
17
- * want to pass declared through unchanged.
18
- */
19
- export type DerivedModeBuilder<A extends ModeAxis> = (u: Record<string, unknown>, declared: ModeValue<A>) => ModeValue<A>;
20
- /**
21
- * Opt-in GPU evaluation marker. When present on a rule, the heap
22
- * scene runs a hard-coded WGSL kernel (see `./gpuKernel.ts`) instead
23
- * of the CPU closure. The closure is retained for fallback (e.g.
24
- * MockGPU tests, devices without compute, or before frame 0).
25
- *
26
- * v1 supports one kernel: `"flipCullByDeterminant"`. v2 generalizes
27
- * via a traced IR builder.
28
- */
29
- export interface GpuRuleSpec<A extends ModeAxis = ModeAxis> {
30
- readonly kernel: "flipCullByDeterminant";
31
- /** Name of the input uniform the kernel reads (e.g. "ModelTrafo"). */
32
- readonly inputUniform: string;
33
- /**
34
- * Declared (SG-scope) value the kernel mutates by. Either a plain
35
- * mode value (baked at scene-build) or an `aval` for reactive
36
- * declared — the dispatcher re-uploads the kernel uniform on each
37
- * dispatch from the aval's current value, so flipping it via a UI
38
- * cval re-runs the rule with the new declared.
39
- */
40
- readonly declared: ModeValue<A> | import("@aardworx/wombat.adaptive").aval<ModeValue<A>>;
41
- }
1
+ import type { RuleExpr } from "@aardworx/wombat.shader";
2
+ import type { aval } from "@aardworx/wombat.adaptive";
3
+ import type { CullMode, FrontFace, Topology, AttachmentBlend } from "../pipelineCache/index.js";
4
+ export type ModeAxis = "cull" | "frontFace" | "topology" | "depthCompare" | "depthWrite" | "alphaToCoverage"
5
+ /** Per-attachment blend state. The rule's u32 output indexes into
6
+ * the `values` array on `DerivedModeOptions` (or the `resolve`
7
+ * callback); each entry is a full {@link AttachmentBlend} struct
8
+ * with srcFactor / dstFactor / operation / writeMask / enabled
9
+ * fields — letting blend rules specify the entire mode object,
10
+ * not just an enum index. */
11
+ | "blend";
12
+ export type ModeValue<A extends ModeAxis> = A extends "cull" ? CullMode : A extends "frontFace" ? FrontFace : A extends "topology" ? Topology : A extends "depthCompare" ? GPUCompareFunction : A extends "depthWrite" ? boolean : A extends "alphaToCoverage" ? boolean : A extends "blend" ? AttachmentBlend : never;
42
13
  export interface DerivedModeRule<A extends ModeAxis = ModeAxis> {
43
14
  readonly __derivedModeRule: true;
44
15
  readonly axis: A;
45
- readonly evaluate: DerivedModeBuilder<A>;
46
- /** Opt-in GPU evaluation. Absent CPU closure path. */
47
- readonly gpu?: GpuRuleSpec<A>;
16
+ /** Shader-IR rule body — the entry's return Expr is the rule. For
17
+ * axes whose `ModeValue<A>` is a struct (e.g. blend's
18
+ * `AttachmentBlend`) the body returns object literals directly;
19
+ * the renderer fingerprints distinct returns and auto-assigns
20
+ * slot indices. For enum axes (cull etc.) the body returns u32
21
+ * enum indices that the renderer maps via the canonical table. */
22
+ readonly expr: RuleExpr<ModeValue<A> | number>;
48
23
  /**
49
- * Conservative over-approximation of the rule's output set. Used by
50
- * the build-time pre-warm in v2 to enumerate the pipelines a scene
51
- * could realize. v1 doesn't use it at runtime, but recording it
52
- * here keeps the API forward-compatible.
24
+ * SG-context value for this axis. Filled in by `<Sg CullMode>` /
25
+ * `<Sg DepthTest>` / etc. at traversal time users never write
26
+ * it. The rule body's `declared` ambient leaf resolves to this;
27
+ * when the underlying aval marks, heapScene re-specialises +
28
+ * swaps the cached kernel + pipeline set.
29
+ *
30
+ * `undefined` is the initial state right after `derivedMode(...)`;
31
+ * the SG fills it in before the rule reaches the renderer.
53
32
  */
54
- readonly domain?: ReadonlyArray<ModeValue<A>>;
33
+ readonly declared: aval<ModeValue<A>> | ModeValue<A> | undefined;
55
34
  }
56
- /**
57
- * Author a derived mode rule.
58
- *
59
- * derivedMode("cull", (u, declared) =>
60
- * u.WindingFlipped ? flipCull(declared) : declared);
61
- *
62
- * derivedMode("blend", (u) => u.Premultiplied ? "premul" : "straight");
63
- *
64
- * // Determinant-flip-cull (the motivating case):
65
- * derivedMode("cull", (u, declared) => {
66
- * const m = u.ModelTrafo as M44d;
67
- * const det =
68
- * m.M00 * (m.M11 * m.M22 - m.M12 * m.M21) -
69
- * m.M01 * (m.M10 * m.M22 - m.M12 * m.M20) +
70
- * m.M02 * (m.M10 * m.M21 - m.M11 * m.M20);
71
- * return det < 0 ? flipCull(declared) : declared;
72
- * });
73
- */
74
- export declare function derivedMode<A extends ModeAxis>(axis: A, evaluate: DerivedModeBuilder<A>, options?: {
75
- readonly domain?: ReadonlyArray<ModeValue<A>>;
76
- }): DerivedModeRule<A>;
77
- /** Brand check — distinguishes a DerivedModeRule from an aval/raw value
78
- * at runtime so PipelineState consumers can route accordingly. */
79
35
  export declare function isDerivedModeRule(x: unknown): x is DerivedModeRule;
80
- /** Convenience: flip 'back' ↔ 'front' (and pass through 'none').
81
- * Useful in `(u, declared) => mirrored ? flipCull(declared) : declared`. */
82
- export declare function flipCull(c: CullMode): CullMode;
83
36
  /**
84
- * Construct a derived-mode rule that flips cullMode based on the sign
85
- * of `det(upperLeft3x3(u.<inputUniform>))`. Runs on the GPU when
86
- * heapScene's compute path is available; the same logic is mirrored
87
- * in the CPU closure for tests and fallback.
37
+ * Brand a `RuleExpr` (the shader-IR artefact emitted by the
38
+ * `rule(...)` build-time marker) as a derived-mode rule for `axis`.
88
39
  *
89
- * const rule = gpuFlipCullByDeterminant("ModelTrafo", "back");
90
- * spec.modeRules = { cull: rule };
40
+ * The `declared` option carries the SG-context value the rule reads
41
+ * via `declare const declared: number` in its body. heapScene
42
+ * substitutes it (as a Const) before codegen — when `declared` is a
43
+ * cval, every mark triggers a re-specialise + cached-kernel swap.
91
44
  *
92
- * Useful for mirrored geometry: scenes that animate a per-RO trafo
93
- * with potentially-negative scale don't pay CPU per-RO determinant
94
- * cost the kernel walks the arena once per frame for ALL ROs that
95
- * carry this rule.
45
+ * `domain` is NOT a parameter: the renderer infers the per-declared
46
+ * set of possible outputs from the rule's IR via
47
+ * `analyseOutputSet` + `evaluateSet`.
96
48
  */
97
- export declare function gpuFlipCullByDeterminant(inputUniform: string, declared?: CullMode | import("@aardworx/wombat.adaptive").aval<CullMode>): DerivedModeRule<"cull">;
49
+ export declare function derivedMode<A extends ModeAxis>(axis: A, expr: RuleExpr<ModeValue<A> | number>): DerivedModeRule<A>;
98
50
  //# sourceMappingURL=rule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/rule.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE/E;;;wDAGwD;AACxD,MAAM,MAAM,QAAQ,GAChB,MAAM,GAAG,WAAW,GAAG,UAAU,GACjC,cAAc,GAAG,YAAY,GAC7B,iBAAiB,CAAC;AAEtB,kCAAkC;AAClC,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ,IACtC,CAAC,SAAS,MAAM,GAAa,QAAQ,GACrC,CAAC,SAAS,WAAW,GAAQ,SAAS,GACtC,CAAC,SAAS,UAAU,GAAS,QAAQ,GACrC,CAAC,SAAS,cAAc,GAAK,kBAAkB,GAC/C,CAAC,SAAS,YAAY,GAAO,OAAO,GACpC,CAAC,SAAS,iBAAiB,GAAG,OAAO,GACrC,KAAK,CAAC;AAER;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,QAAQ,IAAI,CACnD,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1B,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,KACnB,SAAS,CAAC,CAAC,CAAC,CAAC;AAElB;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACxD,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;IACzC,sEAAsE;IACtE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EACb,SAAS,CAAC,CAAC,CAAC,GACZ,OAAO,2BAA2B,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IAC5D,QAAQ,CAAC,iBAAiB,EAAE,IAAI,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAK,CAAC,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;IACzC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAK,WAAW,CAAC,CAAC,CAAC,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,QAAQ,EAC5C,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC/B,OAAO,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;CAAO,GAC9D,eAAe,CAAC,CAAC,CAAC,CAKpB;AAED;mEACmE;AACnE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,eAAe,CAGlE;AAED;6EAC6E;AAC7E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAE9C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,EACpB,QAAQ,GACJ,QAAQ,GACR,OAAO,2BAA2B,EAAE,IAAI,CAAC,QAAQ,CAC3C,GACT,eAAe,CAAC,MAAM,CAAC,CAwBzB"}
1
+ {"version":3,"file":"rule.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/rule.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEhG,MAAM,MAAM,QAAQ,GAChB,MAAM,GAAG,WAAW,GAAG,UAAU,GACjC,cAAc,GAAG,YAAY,GAC7B,iBAAiB;AACnB;;;;;8BAK8B;GAC5B,OAAO,CAAC;AAEZ,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,QAAQ,IACtC,CAAC,SAAS,MAAM,GAAa,QAAQ,GACrC,CAAC,SAAS,WAAW,GAAQ,SAAS,GACtC,CAAC,SAAS,UAAU,GAAS,QAAQ,GACrC,CAAC,SAAS,cAAc,GAAK,kBAAkB,GAC/C,CAAC,SAAS,YAAY,GAAO,OAAO,GACpC,CAAC,SAAS,iBAAiB,GAAG,OAAO,GACrC,CAAC,SAAS,OAAO,GAAY,eAAe,GAC5C,KAAK,CAAC;AAER,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IAC5D,QAAQ,CAAC,iBAAiB,EAAE,IAAI,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB;;;;;uEAKmE;IACnE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/C;;;;;;;;;OASG;IACH,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CAClE;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,eAAe,CAGlE;AAGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,QAAQ,EAC5C,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GACpC,eAAe,CAAC,CAAC,CAAC,CAOpB"}
@@ -1,90 +1,50 @@
1
- // `derivedMode(axis, (u, declared) => modeValue)` author-facing
2
- // marker for per-RO pipeline-state values that are functions of the
3
- // RO's uniforms.
1
+ // Derived-mode rule branded combination of a shader-IR rule
2
+ // expression (from `rule(...)` in wombat.shader) + an axis + the
3
+ // SG-declared aval. The IR is what gets compiled into the partition
4
+ // kernel; the declared aval drives per-frame re-specialisation.
4
5
  //
5
- // v1 is CPU-evaluated: the user's closure runs in JS each time the
6
- // rule's inputs mark. That's a good fit for the typical authoring
7
- // shapes (a uniform-driven enum flip, a determinant-sign cull flip,
8
- // etc.) and pairs naturally with the bucket-level rebuild fast path
9
- // shipped in 0.9.16 — only ROs whose inputs actually changed get
10
- // re-evaluated, and same-key buckets re-bind their pipeline once
11
- // rather than per-RO.
6
+ // Authoring flow (with `@boperators/plugin-vite` + the shader-vite
7
+ // plugin both installed):
12
8
  //
13
- // v2 (deferred) lowers rules to a GPU compute kernel via §7's IR
14
- // machinery needed only when bulk-animated per-RO inputs become a
15
- // CPU-side bottleneck. See docs/derived-mode-rules-plan.md.
9
+ // declare const u: { ModelTrafo: M44f };
10
+ // declare const declared: number;
16
11
  //
17
- // What can be derived (the small-enum axes — cull/blend/depth-compare/
18
- // topology/etc.) and what stays static (depth bias, blend constant,
19
- // stencil ref) is covered in docs/derived-modes.md.
20
- /**
21
- * Author a derived mode rule.
22
- *
23
- * derivedMode("cull", (u, declared) =>
24
- * u.WindingFlipped ? flipCull(declared) : declared);
25
- *
26
- * derivedMode("blend", (u) => u.Premultiplied ? "premul" : "straight");
27
- *
28
- * // Determinant-flip-cull (the motivating case):
29
- * derivedMode("cull", (u, declared) => {
30
- * const m = u.ModelTrafo as M44d;
31
- * const det =
32
- * m.M00 * (m.M11 * m.M22 - m.M12 * m.M21) -
33
- * m.M01 * (m.M10 * m.M22 - m.M12 * m.M20) +
34
- * m.M02 * (m.M10 * m.M21 - m.M11 * m.M20);
35
- * return det < 0 ? flipCull(declared) : declared;
36
- * });
37
- */
38
- export function derivedMode(axis, evaluate, options = {}) {
39
- const out = options.domain !== undefined
40
- ? { __derivedModeRule: true, axis, evaluate, domain: options.domain }
41
- : { __derivedModeRule: true, axis, evaluate };
42
- return out;
43
- }
44
- /** Brand check — distinguishes a DerivedModeRule from an aval/raw value
45
- * at runtime so PipelineState consumers can route accordingly. */
12
+ // const cullExpr = rule(() => {
13
+ // const m: M33f = mat3(u.ModelTrafo);
14
+ // const det = determinant(m);
15
+ // return det < 0 ? flipCull(declared) : declared;
16
+ // });
17
+ //
18
+ // const cullRule = derivedMode("cull", cullExpr, { declared: cullModeC });
19
+ //
20
+ // At runtime, heapScene reads `cullRule.expr.template`, extracts the
21
+ // entry's body Stmt, calls `analyseOutputSet(body)` to obtain the
22
+ // symbolic set, and for each declared-aval value resolves the set
23
+ // via `evaluateSet({"declared": current}, intrinsics)` to size the
24
+ // bucket's slots + codegen the partition kernel.
46
25
  export function isDerivedModeRule(x) {
47
26
  return typeof x === "object" && x !== null
48
27
  && x.__derivedModeRule === true;
49
28
  }
50
- /** Convenience: flip 'back' ↔ 'front' (and pass through 'none').
51
- * Useful in `(u, declared) => mirrored ? flipCull(declared) : declared`. */
52
- export function flipCull(c) {
53
- return c === "back" ? "front" : c === "front" ? "back" : "none";
54
- }
55
29
  /**
56
- * Construct a derived-mode rule that flips cullMode based on the sign
57
- * of `det(upperLeft3x3(u.<inputUniform>))`. Runs on the GPU when
58
- * heapScene's compute path is available; the same logic is mirrored
59
- * in the CPU closure for tests and fallback.
30
+ * Brand a `RuleExpr` (the shader-IR artefact emitted by the
31
+ * `rule(...)` build-time marker) as a derived-mode rule for `axis`.
60
32
  *
61
- * const rule = gpuFlipCullByDeterminant("ModelTrafo", "back");
62
- * spec.modeRules = { cull: rule };
33
+ * The `declared` option carries the SG-context value the rule reads
34
+ * via `declare const declared: number` in its body. heapScene
35
+ * substitutes it (as a Const) before codegen — when `declared` is a
36
+ * cval, every mark triggers a re-specialise + cached-kernel swap.
63
37
  *
64
- * Useful for mirrored geometry: scenes that animate a per-RO trafo
65
- * with potentially-negative scale don't pay CPU per-RO determinant
66
- * cost the kernel walks the arena once per frame for ALL ROs that
67
- * carry this rule.
38
+ * `domain` is NOT a parameter: the renderer infers the per-declared
39
+ * set of possible outputs from the rule's IR via
40
+ * `analyseOutputSet` + `evaluateSet`.
68
41
  */
69
- export function gpuFlipCullByDeterminant(inputUniform, declared = "back") {
42
+ export function derivedMode(axis, expr) {
70
43
  return {
71
44
  __derivedModeRule: true,
72
- axis: "cull",
73
- domain: ["back", "front", "none"],
74
- gpu: { kernel: "flipCullByDeterminant", inputUniform, declared },
75
- evaluate: (u, declaredFromCtx) => {
76
- // Accept Trafo3d ({forward: M44d}) — the wombat.base shape used
77
- // by SG ModelTrafo — or any object with M00..M22 accessors.
78
- const raw = u[inputUniform];
79
- const m = raw.forward
80
- ?? raw;
81
- if (m === undefined || typeof m.M00 !== "number")
82
- return declaredFromCtx;
83
- const det = m.M00 * (m.M11 * m.M22 - m.M12 * m.M21)
84
- - m.M01 * (m.M10 * m.M22 - m.M12 * m.M20)
85
- + m.M02 * (m.M10 * m.M21 - m.M11 * m.M20);
86
- return det < 0 ? flipCull(declaredFromCtx) : declaredFromCtx;
87
- },
45
+ axis,
46
+ expr,
47
+ declared: undefined, // SG fills this in at traversal time
88
48
  };
89
49
  }
90
50
  //# sourceMappingURL=rule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/rule.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,oEAAoE;AACpE,iBAAiB;AACjB,EAAE;AACF,mEAAmE;AACnE,kEAAkE;AAClE,oEAAoE;AACpE,oEAAoE;AACpE,iEAAiE;AACjE,iEAAiE;AACjE,sBAAsB;AACtB,EAAE;AACF,iEAAiE;AACjE,oEAAoE;AACpE,4DAA4D;AAC5D,EAAE;AACF,uEAAuE;AACvE,oEAAoE;AACpE,oDAAoD;AA8EpD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,WAAW,CACzB,IAAO,EACP,QAA+B,EAC/B,UAA6D,EAAE;IAE/D,MAAM,GAAG,GAAuB,OAAO,CAAC,MAAM,KAAK,SAAS;QAC1D,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;QACrE,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAChD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;mEACmE;AACnE,MAAM,UAAU,iBAAiB,CAAC,CAAU;IAC1C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;WAClC,CAAqC,CAAC,iBAAiB,KAAK,IAAI,CAAC;AAC3E,CAAC;AAED;6EAC6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,CAAW;IAClC,OAAO,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CACtC,YAAoB,EACpB,WAGI,MAAM;IAEV,OAAO;QACL,iBAAiB,EAAE,IAAI;QACvB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC;QACjC,GAAG,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,YAAY,EAAE,QAAQ,EAAE;QAChE,QAAQ,EAAE,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE;YAC/B,gEAAgE;YAChE,4DAA4D;YAC5D,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC;YAI5B,MAAM,CAAC,GACJ,GAAuB,CAAC,OAAO;mBAC5B,GAAS,CAAC;YAChB,IAAI,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;gBAAE,OAAO,eAAe,CAAC;YACzE,MAAM,GAAG,GACP,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;kBACvC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;kBACvC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;QAC/D,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/rule.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,iEAAiE;AACjE,oEAAoE;AACpE,gEAAgE;AAChE,EAAE;AACF,mEAAmE;AACnE,0BAA0B;AAC1B,EAAE;AACF,6CAA6C;AAC7C,sCAAsC;AACtC,EAAE;AACF,oCAAoC;AACpC,4CAA4C;AAC5C,oCAAoC;AACpC,wDAAwD;AACxD,UAAU;AACV,EAAE;AACF,+EAA+E;AAC/E,EAAE;AACF,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,mEAAmE;AACnE,iDAAiD;AAmDjD,MAAM,UAAU,iBAAiB,CAAC,CAAU;IAC1C,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;WAClC,CAAqC,CAAC,iBAAiB,KAAK,IAAI,CAAC;AAC3E,CAAC;AAGD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CACzB,IAAO,EACP,IAAqC;IAErC,OAAO;QACL,iBAAiB,EAAE,IAAI;QACvB,IAAI;QACJ,IAAI;QACJ,QAAQ,EAAE,SAAS,EAAG,qCAAqC;KAC5D,CAAC;AACJ,CAAC"}
@@ -3,24 +3,26 @@ import type { RecordsBuffer } from "./records.js";
3
3
  import { type SlotIndex } from "./slots.js";
4
4
  /** Upload the changed-constituent value range to the constituents GPU buffer. */
5
5
  export declare function uploadConstituentsRange(device: GPUDevice, buf: GPUBuffer, mirror: Float32Array, dirty: ReadonlySet<SlotIndex>): void;
6
- /** Live getters for the GPU buffers the kernel binds (scene may grow/replace them). */
6
+ /** Live getters for the scene-wide GPU buffers the kernel binds. The
7
+ * main-heap binding is per-dispatch and supplied to `encodeChunk`,
8
+ * since one DerivedUniformsScene now serves multiple arena chunks
9
+ * (§3) and each chunk has its own main-heap GPUBuffer. */
7
10
  export interface DerivedUniformsResources {
8
11
  /** `array<vec2<f32>>` — df32 trafo halves (the constituents heap). */
9
12
  readonly constituentsBuf: () => GPUBuffer;
10
- /** `array<f32>` — the main heap (drawHeaders); written by the kernel. */
11
- readonly mainHeapBuf: () => GPUBuffer;
12
13
  }
13
14
  export declare class DerivedUniformsDispatcher {
14
15
  private readonly device;
15
16
  private readonly resources;
16
- private readonly gpu;
17
17
  private readonly pipe;
18
- private cachedBg;
19
- private bgKey;
18
+ private readonly byChunk;
20
19
  constructor(device: GPUDevice, resources: DerivedUniformsResources);
21
- /** Open a compute pass and run the uber-kernel. No-ops if there are no records / no rules. */
22
- encode(enc: GPUCommandEncoder, registry: DerivedUniformRegistry, records: RecordsBuffer): boolean;
20
+ /** Run the uber-kernel for ONE chunk's records, binding
21
+ * `mainHeapGetter()` as the heap target. No-op if the chunk's
22
+ * records buffer is empty or the registry has no rules. */
23
+ encodeChunk(enc: GPUCommandEncoder, registry: DerivedUniformRegistry, records: RecordsBuffer, mainHeapGetter: () => GPUBuffer): boolean;
23
24
  dispose(): void;
25
+ private stateFor;
24
26
  private bindGroup;
25
27
  }
26
28
  //# sourceMappingURL=dispatch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/dispatch.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7D,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,GAC5B,IAAI,CAWN;AAED,uFAAuF;AACvF,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,MAAM,SAAS,CAAC;IAC1C,yEAAyE;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,SAAS,CAAC;CACvC;AA2GD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,KAAK,CAA4G;gBAE7G,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,wBAAwB;IAOlE,8FAA8F;IAC9F,MAAM,CAAC,GAAG,EAAE,iBAAiB,EAAE,QAAQ,EAAE,sBAAsB,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO;IAajG,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,SAAS;CAoBlB"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedUniforms/dispatch.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE7D,iFAAiF;AACjF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,GAC5B,IAAI,CAWN;AAED;;;2DAG2D;AAC3D,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,QAAQ,CAAC,eAAe,EAAE,MAAM,SAAS,CAAC;CAC3C;AAmHD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;gBAEpD,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,wBAAwB;IAMlE;;gEAE4D;IAC5D,WAAW,CACT,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,MAAM,SAAS,GAC9B,OAAO;IAcV,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,SAAS;CAmBlB"}
@@ -127,53 +127,66 @@ class UberPipeline {
127
127
  export class DerivedUniformsDispatcher {
128
128
  device;
129
129
  resources;
130
- gpu;
131
130
  pipe;
132
- cachedBg;
133
- bgKey = [undefined, undefined, undefined];
131
+ byChunk = new Map();
134
132
  constructor(device, resources) {
135
133
  this.device = device;
136
134
  this.resources = resources;
137
- this.gpu = new RecordsGpu(device);
138
135
  this.pipe = new UberPipeline(device);
139
136
  }
140
- /** Open a compute pass and run the uber-kernel. No-ops if there are no records / no rules. */
141
- encode(enc, registry, records) {
137
+ /** Run the uber-kernel for ONE chunk's records, binding
138
+ * `mainHeapGetter()` as the heap target. No-op if the chunk's
139
+ * records buffer is empty or the registry has no rules. */
140
+ encodeChunk(enc, registry, records, mainHeapGetter) {
142
141
  if (records.recordCount === 0)
143
142
  return false;
144
143
  const pipeline = this.pipe.pipeline(registry, records.strideWords);
145
144
  if (pipeline === undefined)
146
145
  return false;
147
- this.gpu.sync(records);
146
+ const state = this.stateFor(records);
147
+ state.gpu.sync(records);
148
148
  const pass = enc.beginComputePass({ label: "derivedUniforms.uber" });
149
149
  pass.setPipeline(pipeline);
150
- pass.setBindGroup(0, this.bindGroup());
150
+ pass.setBindGroup(0, this.bindGroup(state, mainHeapGetter()));
151
151
  pass.dispatchWorkgroups(Math.ceil(records.recordCount / 64));
152
152
  pass.end();
153
153
  return true;
154
154
  }
155
155
  dispose() {
156
- this.gpu.dispose();
156
+ for (const s of this.byChunk.values())
157
+ s.gpu.dispose();
158
+ this.byChunk.clear();
157
159
  }
158
- bindGroup() {
160
+ stateFor(records) {
161
+ let s = this.byChunk.get(records);
162
+ if (s === undefined) {
163
+ s = {
164
+ gpu: new RecordsGpu(this.device),
165
+ cachedBg: undefined,
166
+ bgKey: [undefined, undefined, undefined],
167
+ };
168
+ this.byChunk.set(records, s);
169
+ }
170
+ return s;
171
+ }
172
+ bindGroup(state, heap) {
159
173
  const consts = this.resources.constituentsBuf();
160
- const heap = this.resources.mainHeapBuf();
161
- const recs = this.gpu.buffer();
162
- if (this.cachedBg !== undefined && this.bgKey[0] === consts && this.bgKey[1] === heap && this.bgKey[2] === recs) {
163
- return this.cachedBg;
174
+ const recs = state.gpu.buffer();
175
+ if (state.cachedBg !== undefined && state.bgKey[0] === consts && state.bgKey[1] === heap && state.bgKey[2] === recs) {
176
+ return state.cachedBg;
164
177
  }
165
- this.cachedBg = this.device.createBindGroup({
178
+ state.cachedBg = this.device.createBindGroup({
166
179
  label: "derivedUniforms.uber",
167
180
  layout: this.pipe.bgl,
168
181
  entries: [
169
182
  { binding: 0, resource: { buffer: consts } },
170
183
  { binding: 1, resource: { buffer: heap } },
171
184
  { binding: 2, resource: { buffer: recs } },
172
- { binding: 3, resource: { buffer: this.gpu.countBuf } },
185
+ { binding: 3, resource: { buffer: state.gpu.countBuf } },
173
186
  ],
174
187
  });
175
- this.bgKey = [consts, heap, recs];
176
- return this.cachedBg;
188
+ state.bgKey = [consts, heap, recs];
189
+ return state.cachedBg;
177
190
  }
178
191
  }
179
192
  //# sourceMappingURL=dispatch.js.map