@aardworx/wombat.rendering 0.9.14 → 0.9.15

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 (65) hide show
  1. package/dist/runtime/derivedModes/modeKeyCpu.d.ts +45 -0
  2. package/dist/runtime/derivedModes/modeKeyCpu.d.ts.map +1 -0
  3. package/dist/runtime/derivedModes/modeKeyCpu.js +179 -0
  4. package/dist/runtime/derivedModes/modeKeyCpu.js.map +1 -0
  5. package/dist/runtime/derivedModes/partition.d.ts +36 -0
  6. package/dist/runtime/derivedModes/partition.d.ts.map +1 -0
  7. package/dist/runtime/derivedModes/partition.js +170 -0
  8. package/dist/runtime/derivedModes/partition.js.map +1 -0
  9. package/dist/runtime/derivedModes/slotTable.d.ts +70 -0
  10. package/dist/runtime/derivedModes/slotTable.d.ts.map +1 -0
  11. package/dist/runtime/derivedModes/slotTable.js +130 -0
  12. package/dist/runtime/derivedModes/slotTable.js.map +1 -0
  13. package/dist/runtime/heapScene/growBuffer.d.ts +33 -0
  14. package/dist/runtime/heapScene/growBuffer.d.ts.map +1 -0
  15. package/dist/runtime/heapScene/growBuffer.js +76 -0
  16. package/dist/runtime/heapScene/growBuffer.js.map +1 -0
  17. package/dist/runtime/heapScene/packers.d.ts +25 -0
  18. package/dist/runtime/heapScene/packers.d.ts.map +1 -0
  19. package/dist/runtime/heapScene/packers.js +111 -0
  20. package/dist/runtime/heapScene/packers.js.map +1 -0
  21. package/dist/runtime/heapScene/pools.d.ts +181 -0
  22. package/dist/runtime/heapScene/pools.d.ts.map +1 -0
  23. package/dist/runtime/heapScene/pools.js +417 -0
  24. package/dist/runtime/heapScene/pools.js.map +1 -0
  25. package/dist/runtime/heapScene/scanKernel.d.ts +11 -0
  26. package/dist/runtime/heapScene/scanKernel.d.ts.map +1 -0
  27. package/dist/runtime/heapScene/scanKernel.js +181 -0
  28. package/dist/runtime/heapScene/scanKernel.js.map +1 -0
  29. package/dist/runtime/heapScene.d.ts.map +1 -1
  30. package/dist/runtime/heapScene.js +228 -937
  31. package/dist/runtime/heapScene.js.map +1 -1
  32. package/dist/runtime/index.d.ts +4 -0
  33. package/dist/runtime/index.d.ts.map +1 -1
  34. package/dist/runtime/index.js +5 -0
  35. package/dist/runtime/index.js.map +1 -1
  36. package/dist/runtime/pipelineCache/bitfield.d.ts +5 -0
  37. package/dist/runtime/pipelineCache/bitfield.d.ts.map +1 -0
  38. package/dist/runtime/pipelineCache/bitfield.js +201 -0
  39. package/dist/runtime/pipelineCache/bitfield.js.map +1 -0
  40. package/dist/runtime/pipelineCache/cache.d.ts +36 -0
  41. package/dist/runtime/pipelineCache/cache.d.ts.map +1 -0
  42. package/dist/runtime/pipelineCache/cache.js +108 -0
  43. package/dist/runtime/pipelineCache/cache.js.map +1 -0
  44. package/dist/runtime/pipelineCache/descriptor.d.ts +58 -0
  45. package/dist/runtime/pipelineCache/descriptor.d.ts.map +1 -0
  46. package/dist/runtime/pipelineCache/descriptor.js +100 -0
  47. package/dist/runtime/pipelineCache/descriptor.js.map +1 -0
  48. package/dist/runtime/pipelineCache/index.d.ts +5 -0
  49. package/dist/runtime/pipelineCache/index.d.ts.map +1 -0
  50. package/dist/runtime/pipelineCache/index.js +12 -0
  51. package/dist/runtime/pipelineCache/index.js.map +1 -0
  52. package/package.json +1 -1
  53. package/src/runtime/derivedModes/modeKeyCpu.ts +213 -0
  54. package/src/runtime/derivedModes/partition.ts +206 -0
  55. package/src/runtime/derivedModes/slotTable.ts +153 -0
  56. package/src/runtime/heapScene/growBuffer.ts +75 -0
  57. package/src/runtime/heapScene/packers.ts +127 -0
  58. package/src/runtime/heapScene/pools.ts +492 -0
  59. package/src/runtime/heapScene/scanKernel.ts +184 -0
  60. package/src/runtime/heapScene.ts +283 -1064
  61. package/src/runtime/index.ts +40 -0
  62. package/src/runtime/pipelineCache/bitfield.ts +225 -0
  63. package/src/runtime/pipelineCache/cache.ts +129 -0
  64. package/src/runtime/pipelineCache/descriptor.ts +150 -0
  65. package/src/runtime/pipelineCache/index.ts +37 -0
@@ -0,0 +1,45 @@
1
+ import type { PipelineState } from "../../core/pipelineState.js";
2
+ import type { FramebufferSignature } from "../../core/framebufferSignature.js";
3
+ import type { IDisposable } from "@aardworx/wombat.adaptive";
4
+ import { type PipelineStateDescriptor } from "../pipelineCache/index.js";
5
+ /**
6
+ * One-shot snapshot of the current values of every leaf aval in a
7
+ * `PipelineState`, mapped onto a `PipelineStateDescriptor` whose
8
+ * `attachments` array follows the framebuffer's `colorNames` order
9
+ * (missing entries default to non-blended / 0xF writeMask, matching
10
+ * `BLEND_DEFAULT` semantics in heapScene).
11
+ */
12
+ export declare function snapshotDescriptor(ps: PipelineState | undefined, signature: FramebufferSignature): PipelineStateDescriptor;
13
+ /**
14
+ * Subscribes to every leaf aval in a `PipelineState` and invokes
15
+ * `onDirty` when any of them marks. Use one tracker per RO; call
16
+ * `recompute()` after a dirty signal to refresh the descriptor +
17
+ * modeKey, and `dispose()` when the RO is removed.
18
+ *
19
+ * Reactive cvals are the typical case; constant avals also "subscribe"
20
+ * — `addMarkingCallback` is a no-op for an aval that never marks, so
21
+ * no special-casing required.
22
+ */
23
+ export declare class ModeKeyTracker implements IDisposable {
24
+ readonly ps: PipelineState | undefined;
25
+ readonly signature: FramebufferSignature;
26
+ private readonly onDirty;
27
+ private readonly subs;
28
+ private cachedDescriptor;
29
+ private cachedModeKey;
30
+ constructor(ps: PipelineState | undefined, signature: FramebufferSignature, onDirty: () => void);
31
+ get descriptor(): PipelineStateDescriptor;
32
+ get modeKey(): bigint;
33
+ /**
34
+ * Snapshot current aval values and rebuild descriptor + modeKey.
35
+ * Returns true iff the modeKey changed (so the bucket only needs to
36
+ * upload + repartition when something actually moved).
37
+ */
38
+ recompute(): boolean;
39
+ dispose(): void;
40
+ private subscribeAll;
41
+ private sub;
42
+ private subBlendState;
43
+ private subStencilFaces;
44
+ }
45
+ //# sourceMappingURL=modeKeyCpu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modeKeyCpu.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/modeKeyCpu.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAEV,aAAa,EAKd,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGnE,OAAO,EAIL,KAAK,uBAAuB,EAI7B,MAAM,2BAA2B,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,aAAa,GAAG,SAAS,EAC7B,SAAS,EAAE,oBAAoB,GAC9B,uBAAuB,CAsBzB;AAkDD;;;;;;;;;GASG;AACH,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,EAAE,EAAE,aAAa,GAAG,SAAS,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,oBAAoB,CAAC;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,aAAa,CAAS;gBAG5B,EAAE,EAAE,aAAa,GAAG,SAAS,EAC7B,SAAS,EAAE,oBAAoB,EAC/B,OAAO,EAAE,MAAM,IAAI;IAUrB,IAAI,UAAU,IAAI,uBAAuB,CAAkC;IAC3E,IAAI,OAAO,IAAI,MAAM,CAA+B;IAEpD;;;;OAIG;IACH,SAAS,IAAI,OAAO;IASpB,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,eAAe;CAIxB"}
@@ -0,0 +1,179 @@
1
+ // Per-RO modeKey production (CPU side).
2
+ //
3
+ // Task 1 of the derived-modes work: turn a `PipelineState` (the
4
+ // aval-shaped struct from core/pipelineState.ts) plus the surrounding
5
+ // `FramebufferSignature` into a canonical `PipelineStateDescriptor`
6
+ // and its bitfield-encoded modeKey. Subscribes to every input aval
7
+ // so that mutating a `cval` for cullMode / blendFactor / etc. fires
8
+ // a dirty callback, the bucket flushes the changed RO's modeKey to
9
+ // GPU on the next frame, and the partition kernel re-buckets the
10
+ // record into its new slot. No rule-IR machinery yet — Task 2 layers
11
+ // `derivedMode(...)` on top of this.
12
+ import { addMarkingCallback } from "@aardworx/wombat.adaptive";
13
+ import { encodeModeKey, DEFAULT_DESCRIPTOR, DEFAULT_ATTACHMENT_BLEND, } from "../pipelineCache/index.js";
14
+ /**
15
+ * One-shot snapshot of the current values of every leaf aval in a
16
+ * `PipelineState`, mapped onto a `PipelineStateDescriptor` whose
17
+ * `attachments` array follows the framebuffer's `colorNames` order
18
+ * (missing entries default to non-blended / 0xF writeMask, matching
19
+ * `BLEND_DEFAULT` semantics in heapScene).
20
+ */
21
+ export function snapshotDescriptor(ps, signature) {
22
+ if (ps === undefined) {
23
+ return buildAttachmentsFor(DEFAULT_DESCRIPTOR, signature);
24
+ }
25
+ const r = ps.rasterizer;
26
+ const depth = ps.depth !== undefined ? snapshotDepth(ps.depth, signature) : undefined;
27
+ const atc = ps.alphaToCoverage !== undefined ? ps.alphaToCoverage.force( /* allow-force */) : false;
28
+ const blends = ps.blends !== undefined ? ps.blends.force( /* allow-force */) : undefined;
29
+ const attachments = signature.colorNames.map((name) => {
30
+ const b = blends?.tryFind(name);
31
+ return b !== undefined ? snapshotAttachment(b) : DEFAULT_ATTACHMENT_BLEND;
32
+ });
33
+ const out = {
34
+ topology: r.topology.force( /* allow-force */),
35
+ stripIndexFormat: stripFormatFor(r.topology.force( /* allow-force */)),
36
+ frontFace: r.frontFace.force( /* allow-force */),
37
+ cullMode: r.cullMode.force( /* allow-force */),
38
+ ...(depth !== undefined ? { depth } : {}),
39
+ attachments,
40
+ alphaToCoverage: atc,
41
+ };
42
+ return out;
43
+ }
44
+ function buildAttachmentsFor(base, signature) {
45
+ // When `ps` is undefined, every named attachment uses defaults.
46
+ return {
47
+ ...base,
48
+ attachments: signature.colorNames.map(() => DEFAULT_ATTACHMENT_BLEND),
49
+ };
50
+ }
51
+ function snapshotDepth(d, signature) {
52
+ if (signature.depthStencil === undefined)
53
+ return undefined;
54
+ return {
55
+ write: d.write.force( /* allow-force */),
56
+ compare: d.compare.force( /* allow-force */),
57
+ clamp: d.clamp !== undefined ? d.clamp.force( /* allow-force */) : false,
58
+ };
59
+ }
60
+ function snapshotAttachment(b) {
61
+ const color = snapshotBlendComponent(b.color);
62
+ const alpha = snapshotBlendComponent(b.alpha);
63
+ const writeMask = b.writeMask.force( /* allow-force */) & 0xF;
64
+ // "Blend enabled" derives from whether the components differ from
65
+ // the no-op (src=one, dst=zero, op=add) shape. This mirrors WebGPU's
66
+ // contract: a blend descriptor MUST be present for blending to occur,
67
+ // and the no-op shape is the canonical disabled state.
68
+ const enabled = !(color.srcFactor === "one" && color.dstFactor === "zero" && color.operation === "add" &&
69
+ alpha.srcFactor === "one" && alpha.dstFactor === "zero" && alpha.operation === "add");
70
+ return { enabled, color, alpha, writeMask };
71
+ }
72
+ function snapshotBlendComponent(c) {
73
+ return {
74
+ srcFactor: c.srcFactor.force( /* allow-force */),
75
+ dstFactor: c.dstFactor.force( /* allow-force */),
76
+ operation: c.operation.force( /* allow-force */),
77
+ };
78
+ }
79
+ function stripFormatFor(topology) {
80
+ return topology === "line-strip" || topology === "triangle-strip" ? "uint32" : undefined;
81
+ }
82
+ // ─── Reactive tracker ──────────────────────────────────────────────────
83
+ /**
84
+ * Subscribes to every leaf aval in a `PipelineState` and invokes
85
+ * `onDirty` when any of them marks. Use one tracker per RO; call
86
+ * `recompute()` after a dirty signal to refresh the descriptor +
87
+ * modeKey, and `dispose()` when the RO is removed.
88
+ *
89
+ * Reactive cvals are the typical case; constant avals also "subscribe"
90
+ * — `addMarkingCallback` is a no-op for an aval that never marks, so
91
+ * no special-casing required.
92
+ */
93
+ export class ModeKeyTracker {
94
+ ps;
95
+ signature;
96
+ onDirty;
97
+ subs = [];
98
+ cachedDescriptor;
99
+ cachedModeKey;
100
+ constructor(ps, signature, onDirty) {
101
+ this.ps = ps;
102
+ this.signature = signature;
103
+ this.onDirty = onDirty;
104
+ this.cachedDescriptor = snapshotDescriptor(ps, signature);
105
+ this.cachedModeKey = encodeModeKey(this.cachedDescriptor);
106
+ this.subscribeAll();
107
+ }
108
+ get descriptor() { return this.cachedDescriptor; }
109
+ get modeKey() { return this.cachedModeKey; }
110
+ /**
111
+ * Snapshot current aval values and rebuild descriptor + modeKey.
112
+ * Returns true iff the modeKey changed (so the bucket only needs to
113
+ * upload + repartition when something actually moved).
114
+ */
115
+ recompute() {
116
+ const next = snapshotDescriptor(this.ps, this.signature);
117
+ const nextKey = encodeModeKey(next);
118
+ if (nextKey === this.cachedModeKey)
119
+ return false;
120
+ this.cachedDescriptor = next;
121
+ this.cachedModeKey = nextKey;
122
+ return true;
123
+ }
124
+ dispose() {
125
+ for (const s of this.subs)
126
+ s.dispose();
127
+ this.subs.length = 0;
128
+ }
129
+ subscribeAll() {
130
+ if (this.ps === undefined)
131
+ return;
132
+ const ps = this.ps;
133
+ this.sub(ps.rasterizer.topology);
134
+ this.sub(ps.rasterizer.cullMode);
135
+ this.sub(ps.rasterizer.frontFace);
136
+ if (ps.rasterizer.depthBias !== undefined)
137
+ this.sub(ps.rasterizer.depthBias);
138
+ if (ps.depth !== undefined) {
139
+ this.sub(ps.depth.write);
140
+ this.sub(ps.depth.compare);
141
+ if (ps.depth.clamp !== undefined)
142
+ this.sub(ps.depth.clamp);
143
+ }
144
+ if (ps.stencil !== undefined) {
145
+ // Stencil enabled/reference/masks contribute to neither modeKey
146
+ // nor cache key (v1 carves stencil out — only the "enabled"
147
+ // flag matters and we surface it via snapshotDescriptor). We
148
+ // still subscribe so callers can repackage if a future change
149
+ // flips the enable bit.
150
+ this.subStencilFaces(ps.stencil);
151
+ }
152
+ if (ps.blends !== undefined) {
153
+ this.sub(ps.blends);
154
+ const map = ps.blends.force( /* allow-force */);
155
+ for (const [, bs] of map)
156
+ this.subBlendState(bs);
157
+ }
158
+ if (ps.alphaToCoverage !== undefined)
159
+ this.sub(ps.alphaToCoverage);
160
+ // blendConstant is dynamic state; not in the modeKey, no subscription needed.
161
+ }
162
+ sub(a) {
163
+ this.subs.push(addMarkingCallback(a, this.onDirty));
164
+ }
165
+ subBlendState(b) {
166
+ this.sub(b.color.srcFactor);
167
+ this.sub(b.color.dstFactor);
168
+ this.sub(b.color.operation);
169
+ this.sub(b.alpha.srcFactor);
170
+ this.sub(b.alpha.dstFactor);
171
+ this.sub(b.alpha.operation);
172
+ this.sub(b.writeMask);
173
+ }
174
+ subStencilFaces(_s) {
175
+ // v1: stencil isn't in the modeKey; deliberately not subscribing
176
+ // to face avals here. Wire up in v2 when stencil enters the key.
177
+ }
178
+ }
179
+ //# sourceMappingURL=modeKeyCpu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modeKeyCpu.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/modeKeyCpu.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,gEAAgE;AAChE,sEAAsE;AACtE,oEAAoE;AACpE,mEAAmE;AACnE,oEAAoE;AACpE,mEAAmE;AACnE,iEAAiE;AACjE,qEAAqE;AACrE,qCAAqC;AAYrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,wBAAwB,GAKzB,MAAM,2BAA2B,CAAC;AAEnC;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,EAA6B,EAC7B,SAA+B;IAE/B,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACrB,OAAO,mBAAmB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC;IACxB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,GAAG,GAAG,EAAE,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,EAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnG,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,EAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxF,MAAM,WAAW,GAAsB,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACvE,MAAM,CAAC,GAAG,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB,CAAC;IAC5E,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAA4B;QACnC,QAAQ,EAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAC,iBAAiB,CAAC;QACrD,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAC,iBAAiB,CAAC,CAAC;QACrE,SAAS,EAAS,CAAC,CAAC,SAAS,CAAC,KAAK,EAAC,iBAAiB,CAAC;QACtD,QAAQ,EAAU,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAC,iBAAiB,CAAC;QACrD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,WAAW;QACX,eAAe,EAAE,GAAG;KACrB,CAAC;IACF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,mBAAmB,CAC1B,IAA6B,EAC7B,SAA+B;IAE/B,gEAAgE;IAChE,OAAO;QACL,GAAG,IAAI;QACP,WAAW,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,wBAAwB,CAAC;KACtE,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,CAAa,EAAE,SAA+B;IACnE,IAAI,SAAS,CAAC,YAAY,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO;QACL,KAAK,EAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAC,iBAAiB,CAAC;QACzC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAC,iBAAiB,CAAC;QAC3C,KAAK,EAAI,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,EAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,KAAK;KAC1E,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAe;IACzC,MAAM,KAAK,GAAG,sBAAsB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,sBAAsB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,EAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC;IAC7D,kEAAkE;IAClE,qEAAqE;IACrE,sEAAsE;IACtE,uDAAuD;IACvD,MAAM,OAAO,GACX,CAAC,CAAC,KAAK,CAAC,SAAS,KAAK,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK;QACpF,KAAK,CAAC,SAAS,KAAK,KAAK,IAAI,KAAK,CAAC,SAAS,KAAK,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC;IAC1F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9C,CAAC;AAED,SAAS,sBAAsB,CAAC,CAAwB;IACtD,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,EAAC,iBAAiB,CAAC;QAC/C,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,EAAC,iBAAiB,CAAC;QAC/C,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,EAAC,iBAAiB,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,QAA8B;IACpD,OAAO,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3F,CAAC;AAED,0EAA0E;AAE1E;;;;;;;;;GASG;AACH,MAAM,OAAO,cAAc;IAChB,EAAE,CAA4B;IAC9B,SAAS,CAAuB;IACxB,OAAO,CAAa;IACpB,IAAI,GAAkB,EAAE,CAAC;IAClC,gBAAgB,CAA0B;IAC1C,aAAa,CAAS;IAE9B,YACE,EAA6B,EAC7B,SAA+B,EAC/B,OAAmB;QAEnB,IAAI,CAAC,EAAE,GAAU,EAAE,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAK,OAAO,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,kBAAkB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC,aAAa,GAAM,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7D,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,UAAU,KAA8B,OAAO,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC3E,IAAI,OAAO,KAAa,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAEpD;;;;OAIG;IACH,SAAS;QACP,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,IAAI,CAAC,aAAa;YAAE,OAAO,KAAK,CAAC;QACjD,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAM,OAAO,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACvB,CAAC;IAEO,YAAY;QAClB,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS;YAAE,OAAO;QAClC,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,KAAK,SAAS;YAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC7E,IAAI,EAAE,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,EAAE,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC7B,gEAAgE;YAChE,4DAA4D;YAC5D,6DAA6D;YAC7D,8DAA8D;YAC9D,wBAAwB;YACxB,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,EAAE,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YACpB,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAC,iBAAiB,CAAC,CAAC;YAC/C,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG;gBAAE,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,EAAE,CAAC,eAAe,KAAK,SAAS;YAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC;QACnE,8EAA8E;IAChF,CAAC;IAEO,GAAG,CAAC,CAAgB;QAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAEO,aAAa,CAAC,CAAe;QACnC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACtF,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACtF,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC;IAEO,eAAe,CAAC,EAAgB;QACtC,iEAAiE;QACjE,iEAAiE;IACnE,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ /** v1 cap on distinct pipeline slots per bucket. */
2
+ export declare const MAX_SLOTS_PER_BUCKET = 256;
3
+ export interface PartitionInput {
4
+ /** Per-record slot index (length = numRecords). */
5
+ readonly slot: ReadonlyArray<number>;
6
+ /** Per-record (indexCount * instanceCount) — emit count contributed
7
+ * to its slot's drawIndirect vertex total. */
8
+ readonly emitCount: ReadonlyArray<number>;
9
+ /** Number of distinct slots used. Slots outside this range produce
10
+ * an error. */
11
+ readonly slotCount: number;
12
+ }
13
+ export interface PartitionResult {
14
+ /** Per-slot record count. Length = slotCount. */
15
+ readonly slotCounts: Uint32Array;
16
+ /** Per-slot cumulative record-count prefix sum (length = slotCount + 1). */
17
+ readonly slotOffsets: Uint32Array;
18
+ /** Per-slot total emit count (vertex count for the drawIndirect). */
19
+ readonly slotTotalEmit: Uint32Array;
20
+ /** Permuted record indices, grouped by slot. Length = numRecords. */
21
+ readonly slotRecords: Uint32Array;
22
+ /** Slot-local prefix sum of emitCounts, length = numRecords + slotCount.
23
+ * For slot s, the prefix array is
24
+ * slotEmitPrefix[slotOffsets[s] + s ..= slotOffsets[s+1] + s]
25
+ * (one extra entry per slot for the upper bound). */
26
+ readonly slotEmitPrefix: Uint32Array;
27
+ }
28
+ /**
29
+ * Pure-CPU partition. Reference implementation for the WGSL kernel
30
+ * below. Order within a slot is stable wrt. input record order so
31
+ * tests can predict the permuted layout deterministically.
32
+ */
33
+ export declare function partitionCPU(input: PartitionInput): PartitionResult;
34
+ export declare const PARTITION_HISTOGRAM_WGSL = "\nstruct PartitionInputs {\n numRecords: u32,\n slotCount: u32,\n _pad0: u32,\n _pad1: u32,\n};\n@group(0) @binding(0) var<uniform> uIn: PartitionInputs;\n@group(0) @binding(1) var<storage, read> slot: array<u32>;\n@group(0) @binding(2) var<storage, read> emitCount: array<u32>;\n@group(0) @binding(3) var<storage, read_write> slotCounts: array<atomic<u32>>;\n@group(0) @binding(4) var<storage, read_write> slotTotalEmit: array<atomic<u32>>;\n\n@compute @workgroup_size(64)\nfn histogram(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x;\n if (i >= uIn.numRecords) { return; }\n let s = slot[i];\n atomicAdd(&slotCounts[s], 1u);\n atomicAdd(&slotTotalEmit[s], emitCount[i]);\n}\n";
35
+ export declare const PARTITION_SCATTER_WGSL = "\nstruct PartitionInputs {\n numRecords: u32,\n slotCount: u32,\n _pad0: u32,\n _pad1: u32,\n};\n@group(0) @binding(0) var<uniform> uIn: PartitionInputs;\n@group(0) @binding(1) var<storage, read> slot: array<u32>;\n@group(0) @binding(2) var<storage, read> slotOffsets: array<u32>;\n@group(0) @binding(3) var<storage, read_write> cursors: array<atomic<u32>>;\n@group(0) @binding(4) var<storage, read_write> slotRecords: array<u32>;\n\n@compute @workgroup_size(64)\nfn scatter(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x;\n if (i >= uIn.numRecords) { return; }\n let s = slot[i];\n let base = slotOffsets[s];\n let k = atomicAdd(&cursors[s], 1u);\n slotRecords[base + k] = i;\n}\n";
36
+ //# sourceMappingURL=partition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partition.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/partition.ts"],"names":[],"mappings":"AAgCA,oDAAoD;AACpD,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAW,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;kDAC8C;IAC9C,QAAQ,CAAC,SAAS,EAAM,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C;mBACe;IACf,QAAQ,CAAC,SAAS,EAAM,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAM,WAAW,CAAC;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAK,WAAW,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAG,WAAW,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAK,WAAW,CAAC;IACrC;;;yDAGqD;IACrD,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CAqDnE;AAwCD,eAAO,MAAM,wBAAwB,uwBAqBpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,2wBAsBlC,CAAC"}
@@ -0,0 +1,170 @@
1
+ // Per-frame partition kernel — turns per-RO slot assignments into
2
+ // per-slot draw metadata (record lists + cumulative emit prefix sums
3
+ // + total indexCount × instanceCount per slot).
4
+ //
5
+ // Each bucket runs this kernel once per frame (dirty-gated). Output
6
+ // feeds one `drawIndirect` per slot in the encode loop. Records routed
7
+ // to the same slot share an indirect call; records in different slots
8
+ // land in separate `setPipeline + drawIndirect` calls.
9
+ //
10
+ // The CPU function `partitionCPU` is the algorithmic reference and
11
+ // the basis for unit tests. The WGSL kernel below mirrors the same
12
+ // algorithm using two compute passes (histogram + scatter), with an
13
+ // exclusive prefix scan on the histogram in between.
14
+ //
15
+ // Layout choices:
16
+ //
17
+ // - Records are NOT physically re-sorted. Instead, the kernel writes
18
+ // a permuted index list `slotRecords[]` (one u32 per record), plus
19
+ // per-slot offsets `slotOffsets[]` and counts `slotCounts[]`. The
20
+ // existing drawTable stays put — the VS prelude does one extra
21
+ // read of `slotRecords[…]` to map from "drawIndex within slot"
22
+ // back to "drawIndex in the full drawTable."
23
+ // - `slotEmitPrefix[]` is the slot-local prefix-sum of
24
+ // `record.indexCount * record.instanceCount`. The VS prelude
25
+ // binary-searches THIS (per-slot) instead of the global table.
26
+ // - `slotTotalEmit[]` is the dispatched vertex count for that
27
+ // slot's drawIndirect.
28
+ //
29
+ // Buffers grow lazily; the partition wrapper resizes as slot counts
30
+ // or record counts change. v1 caps slots per bucket at MAX_SLOTS
31
+ // (256), exceeded slots throw with a friendly diagnostic.
32
+ /** v1 cap on distinct pipeline slots per bucket. */
33
+ export const MAX_SLOTS_PER_BUCKET = 256;
34
+ /**
35
+ * Pure-CPU partition. Reference implementation for the WGSL kernel
36
+ * below. Order within a slot is stable wrt. input record order so
37
+ * tests can predict the permuted layout deterministically.
38
+ */
39
+ export function partitionCPU(input) {
40
+ const { slot, emitCount, slotCount } = input;
41
+ const numRecords = slot.length;
42
+ if (emitCount.length !== numRecords) {
43
+ throw new Error("partition: slot.length != emitCount.length");
44
+ }
45
+ if (slotCount > MAX_SLOTS_PER_BUCKET) {
46
+ throw new Error(`partition: slotCount ${slotCount} exceeds cap ${MAX_SLOTS_PER_BUCKET}`);
47
+ }
48
+ const slotCounts = new Uint32Array(slotCount);
49
+ const slotOffsets = new Uint32Array(slotCount + 1);
50
+ const slotTotalEmit = new Uint32Array(slotCount);
51
+ const slotRecords = new Uint32Array(numRecords);
52
+ const slotEmitPrefix = new Uint32Array(numRecords + slotCount);
53
+ // Pass 1: histogram + per-slot emit sum.
54
+ for (let i = 0; i < numRecords; i++) {
55
+ const s = slot[i];
56
+ if (s < 0 || s >= slotCount) {
57
+ throw new Error(`partition: record ${i} has out-of-range slot ${s} (slotCount=${slotCount})`);
58
+ }
59
+ slotCounts[s] = slotCounts[s] + 1;
60
+ slotTotalEmit[s] = slotTotalEmit[s] + emitCount[i];
61
+ }
62
+ // Pass 2: exclusive prefix sum -> per-slot start offsets.
63
+ let acc = 0;
64
+ for (let s = 0; s < slotCount; s++) {
65
+ slotOffsets[s] = acc;
66
+ acc += slotCounts[s];
67
+ }
68
+ slotOffsets[slotCount] = acc;
69
+ // Pass 3: scatter — second pass over records to place each at its
70
+ // slot's current cursor, emitting the within-slot prefix sums.
71
+ const cursors = new Uint32Array(slotCount);
72
+ for (let i = 0; i < numRecords; i++) {
73
+ const s = slot[i];
74
+ const base = slotOffsets[s]; // first slotRecords[] index for this slot
75
+ const prefBase = base + s; // first slotEmitPrefix[] index for this slot
76
+ const k = cursors[s]; // within-slot position
77
+ slotRecords[base + k] = i;
78
+ // Slot prefix sums are length (slotCounts[s] + 1): first entry
79
+ // is 0, then cumulative; the last entry equals slotTotalEmit[s].
80
+ if (k === 0) {
81
+ slotEmitPrefix[prefBase] = 0;
82
+ }
83
+ slotEmitPrefix[prefBase + k + 1] = slotEmitPrefix[prefBase + k] + emitCount[i];
84
+ cursors[s] = k + 1;
85
+ }
86
+ return { slotCounts, slotOffsets, slotTotalEmit, slotRecords, slotEmitPrefix };
87
+ }
88
+ // ─── WGSL kernel ───────────────────────────────────────────────────────
89
+ //
90
+ // Mirrors `partitionCPU` in two compute passes:
91
+ //
92
+ // Pass A (histogram):
93
+ // - one thread per record
94
+ // - read slot[i], emitCount[i]
95
+ // - atomicAdd(slotCounts[s], 1)
96
+ // - atomicAdd(slotTotalEmit[s], emitCount[i])
97
+ //
98
+ // Pass B (scatter):
99
+ // - one thread per record
100
+ // - look up slot[i]
101
+ // - atomicAdd(cursors[s], 1) -> within-slot position k
102
+ // - slotRecords[slotOffsets[s] + k] = i
103
+ // - slotEmitPrefix[slotOffsets[s] + s + k + 1] is computed by a
104
+ // second scan pass (the within-slot prefix sum). v1 emits a
105
+ // serial CPU-side scan for simplicity; v2 should fold this into
106
+ // a per-slot warp-scan once buckets get large.
107
+ //
108
+ // Between the two passes, the CPU runs an exclusive scan on
109
+ // `slotCounts` to produce `slotOffsets` (small array, ≤ MAX_SLOTS).
110
+ //
111
+ // The within-slot prefix scan in pass B uses a non-atomic read of
112
+ // `slotEmitPrefix[prefBase + k]` — this is safe because the only
113
+ // thread that writes index `prefBase + k` is the one whose `cursors`
114
+ // fetchAdd returned `k`, and that thread completed before any thread
115
+ // whose fetchAdd returned `k+1` could read `prefBase + k`.
116
+ //
117
+ // (Wait — that's only true if we have a memory barrier across
118
+ // invocations, which WGSL doesn't guarantee inside one dispatch. In
119
+ // practice v1 punts: the kernel emits the histogram + slotRecords,
120
+ // and the host or a follow-up pass computes slotEmitPrefix. The
121
+ // kernel below reflects this: it produces slotCounts, slotOffsets
122
+ // (after host scan), slotTotalEmit, and slotRecords. The
123
+ // slotEmitPrefix scan is performed in a third compute pass on the
124
+ // already-scattered records.)
125
+ export const PARTITION_HISTOGRAM_WGSL = /* wgsl */ `
126
+ struct PartitionInputs {
127
+ numRecords: u32,
128
+ slotCount: u32,
129
+ _pad0: u32,
130
+ _pad1: u32,
131
+ };
132
+ @group(0) @binding(0) var<uniform> uIn: PartitionInputs;
133
+ @group(0) @binding(1) var<storage, read> slot: array<u32>;
134
+ @group(0) @binding(2) var<storage, read> emitCount: array<u32>;
135
+ @group(0) @binding(3) var<storage, read_write> slotCounts: array<atomic<u32>>;
136
+ @group(0) @binding(4) var<storage, read_write> slotTotalEmit: array<atomic<u32>>;
137
+
138
+ @compute @workgroup_size(64)
139
+ fn histogram(@builtin(global_invocation_id) gid: vec3<u32>) {
140
+ let i = gid.x;
141
+ if (i >= uIn.numRecords) { return; }
142
+ let s = slot[i];
143
+ atomicAdd(&slotCounts[s], 1u);
144
+ atomicAdd(&slotTotalEmit[s], emitCount[i]);
145
+ }
146
+ `;
147
+ export const PARTITION_SCATTER_WGSL = /* wgsl */ `
148
+ struct PartitionInputs {
149
+ numRecords: u32,
150
+ slotCount: u32,
151
+ _pad0: u32,
152
+ _pad1: u32,
153
+ };
154
+ @group(0) @binding(0) var<uniform> uIn: PartitionInputs;
155
+ @group(0) @binding(1) var<storage, read> slot: array<u32>;
156
+ @group(0) @binding(2) var<storage, read> slotOffsets: array<u32>;
157
+ @group(0) @binding(3) var<storage, read_write> cursors: array<atomic<u32>>;
158
+ @group(0) @binding(4) var<storage, read_write> slotRecords: array<u32>;
159
+
160
+ @compute @workgroup_size(64)
161
+ fn scatter(@builtin(global_invocation_id) gid: vec3<u32>) {
162
+ let i = gid.x;
163
+ if (i >= uIn.numRecords) { return; }
164
+ let s = slot[i];
165
+ let base = slotOffsets[s];
166
+ let k = atomicAdd(&cursors[s], 1u);
167
+ slotRecords[base + k] = i;
168
+ }
169
+ `;
170
+ //# sourceMappingURL=partition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partition.js","sourceRoot":"","sources":["../../../src/runtime/derivedModes/partition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { PipelineCache, type PipelineBuilder, type PipelineStateDescriptor } from "../pipelineCache/index.js";
2
+ export interface SlotTableEntry {
3
+ readonly slotIndex: number;
4
+ readonly modeKey: bigint;
5
+ readonly descriptor: PipelineStateDescriptor;
6
+ /** Refcount of ROs currently bound to this slot. v1 doesn't GC at 0. */
7
+ refCount: number;
8
+ /** Populated lazily by precompile; null until linked. */
9
+ pipeline: GPURenderPipeline | null;
10
+ }
11
+ /**
12
+ * One SlotTable per `(effect, textureSet)` bucket. The cache may be
13
+ * shared across buckets — pipelines that happen to share a descriptor
14
+ * (rare across distinct effects, since pipeline objects bake the
15
+ * shader / layout) still dedupe at the cache level if a builder
16
+ * legitimately produces the same GPURenderPipeline for the same
17
+ * descriptor key.
18
+ */
19
+ export declare class SlotTable {
20
+ readonly cache: PipelineCache;
21
+ readonly builder: PipelineBuilder;
22
+ private readonly entries;
23
+ private readonly byKey;
24
+ /** Tracks pending precompile promises so ready() waits for the union. */
25
+ private pending;
26
+ private version;
27
+ constructor(cache: PipelineCache, builder: PipelineBuilder);
28
+ /** Total slot count (linked or pending). */
29
+ get slotCount(): number;
30
+ /**
31
+ * Bumps whenever a new slot is appended. Consumers (partition setup,
32
+ * indirect-args resize, GPU lookup-table upload) compare against
33
+ * their last-seen value to know when to re-upload.
34
+ */
35
+ get layoutVersion(): number;
36
+ /** Iterate all slots in slot-index order. */
37
+ all(): IterableIterator<SlotTableEntry>;
38
+ /**
39
+ * Add (or reference-bump) the slot for `descriptor`. Returns the
40
+ * existing slot if it already exists, otherwise creates one and
41
+ * kicks off compilation via `builder` (async by default).
42
+ *
43
+ * Pass `sync: true` on the runtime-mutation path to use
44
+ * `builder.createSync` instead — the returned slot's `pipeline` will
45
+ * be populated immediately.
46
+ */
47
+ addRO(descriptor: PipelineStateDescriptor, options?: {
48
+ sync?: boolean;
49
+ }): SlotTableEntry;
50
+ /** Drop one reference from the slot. Pipelines stay resident. */
51
+ removeRO(modeKey: bigint): void;
52
+ /**
53
+ * Move an RO from one slot to another (reactive aval mutation
54
+ * caused its modeKey to change). Returns the new (possibly newly
55
+ * allocated) slot.
56
+ */
57
+ rebindRO(fromModeKey: bigint, toDescriptor: PipelineStateDescriptor, options?: {
58
+ sync?: boolean;
59
+ }): SlotTableEntry;
60
+ /** Sync slot lookup by modeKey. Returns the entry or null. */
61
+ lookup(modeKey: bigint): SlotTableEntry | null;
62
+ /**
63
+ * Resolves when every currently-registered slot has a linked
64
+ * pipeline. Subsequent `addRO` calls after this resolves with
65
+ * `sync: false` will produce fresh pending work; call `ready()`
66
+ * again to wait.
67
+ */
68
+ ready(): Promise<void>;
69
+ }
70
+ //# sourceMappingURL=slotTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slotTable.d.ts","sourceRoot":"","sources":["../../../src/runtime/derivedModes/slotTable.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,aAAa,EAEb,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC7B,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAI,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;IAC7C,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,qBAAa,SAAS;IAQlB,QAAQ,CAAC,KAAK,EAAE,aAAa;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe;IARnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAC7D,yEAAyE;IACzE,OAAO,CAAC,OAAO,CAAoC;IACnD,OAAO,CAAC,OAAO,CAAK;gBAGT,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,eAAe;IAGnC,4CAA4C;IAC5C,IAAI,SAAS,IAAI,MAAM,CAAgC;IACvD;;;;OAIG;IACH,IAAI,aAAa,IAAI,MAAM,CAAyB;IACpD,6CAA6C;IAC5C,GAAG,IAAI,gBAAgB,CAAC,cAAc,CAAC;IAExC;;;;;;;;OAQG;IACH,KAAK,CACH,UAAU,EAAE,uBAAuB,EACnC,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GAC/B,cAAc;IA8BjB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM/B;;;;OAIG;IACH,QAAQ,CACN,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,uBAAuB,EACrC,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO,GAC/B,cAAc;IAKjB,8DAA8D;IAC9D,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAI9C;;;;;OAKG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B"}