@aardworx/wombat.rendering 0.21.3 → 0.21.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aardworx/wombat.rendering",
3
- "version": "0.21.3",
4
- "description": "WebGPU rendering layer for the Wombat TypeScript stack \u2014 RenderObject/RenderTask/runtime + window glue, port of Aardvark.Rendering's lower layers on top of WebGPU and wombat.shader.",
3
+ "version": "0.21.4",
4
+ "description": "WebGPU rendering layer for the Wombat TypeScript stack RenderObject/RenderTask/runtime + window glue, port of Aardvark.Rendering's lower layers on top of WebGPU and wombat.shader.",
5
5
  "license": "MIT",
6
6
  "author": "krauthaufen",
7
7
  "repository": {
@@ -66,4 +66,4 @@
66
66
  "access": "public"
67
67
  },
68
68
  "private": false
69
- }
69
+ }
@@ -129,4 +129,29 @@ export interface RenderObject {
129
129
  * See docs/gpu-transform-propagation.md.
130
130
  */
131
131
  readonly modelChain?: readonly aval<import("@aardworx/wombat.base").Trafo3d>[];
132
+ /**
133
+ * Producer-asserted heap eligibility. When `true`, the hybrid
134
+ * partition routes this RO to the heap path WITHOUT building the
135
+ * per-RO reactive `isHeapEligible` predicate — for an RO with an
136
+ * adaptive `drawCall` that predicate is a live custom aval + a
137
+ * negation aval + their subscriptions, retained per RO for the
138
+ * scene's lifetime; at collection scale (thousands of row-shaped
139
+ * ROs) that is pure ballast because the producer already knows the
140
+ * answer statically.
141
+ *
142
+ * The producer GUARANTEES, for the RO's whole lifetime:
143
+ * - every attribute/index buffer aval resolves to `kind: "host"`,
144
+ * - ≤ 1 distinct texture aval and ≤ 1 distinct sampler aval,
145
+ * each heap-servable; no `storageBuffers`,
146
+ * - index format (if indexed) is uint16/uint32,
147
+ * - `drawCall.firstInstance === 0`; non-indexed ⇒
148
+ * `firstVertex === 0`; `instanceCount ≥ 0` (0 draws nothing),
149
+ * - payload stays within the heap ingest budget.
150
+ *
151
+ * Only the global `heapEnabled` toggle still applies (one shared
152
+ * aval per scene). A wrong assertion produces wrong routing —
153
+ * intended for scene-graph layers that verify these conditions
154
+ * once per template, never for hand-built ROs.
155
+ */
156
+ readonly heapAsserted?: boolean;
132
157
  }
@@ -605,7 +605,11 @@ export function renderObjectToHeapSpec(
605
605
  pipelineState: ro.pipelineState,
606
606
  inputs,
607
607
  ...(instanceAttributes !== undefined ? { instanceAttributes } : {}),
608
- ...(dc.instanceCount > 1 ? { instanceCount: dc.instanceCount } : {}),
608
+ // 1 (not > 1): instanceCount 0 must flow through — the scan
609
+ // kernel emits indexCount × 0 = nothing, matching what the legacy
610
+ // path draws for zero instances. Omitting it would default to 1
611
+ // and pack a garbage instance from empty per-instance arrays.
612
+ ...(dc.instanceCount !== 1 ? { instanceCount: dc.instanceCount } : {}),
609
613
  ...geom,
610
614
  ...(textures !== undefined ? { textures } : {}),
611
615
  ...(ro.injectedStorage !== undefined && ro.injectedStorage.count > 0
@@ -202,9 +202,16 @@ export function compileHybridScene(
202
202
  // The per-RO eligibility is ANDed with the global `heapEnabled`
203
203
  // toggle — flipping that off forces every RO to legacy.
204
204
  const heapEnabled = opts.heapEnabled ?? AVal.constant(true);
205
+ // Producer-asserted ROs (`heapAsserted: true`) skip the per-RO
206
+ // predicate entirely — they all share these TWO scene-level avals
207
+ // instead of two live avals each (see RenderObject.heapAsserted).
208
+ const notHeapEnabled = heapEnabled.isConstant
209
+ ? AVal.constant(!heapEnabled.force(/* allow-force */))
210
+ : heapEnabled.map(b => !b);
205
211
  const eligCache = new WeakMap<RenderObject, aval<boolean>>();
206
212
  const notEligCache = new WeakMap<RenderObject, aval<boolean>>();
207
213
  const elig = (ro: RenderObject): aval<boolean> => {
214
+ if (ro.heapAsserted === true) return heapEnabled;
208
215
  let av = eligCache.get(ro);
209
216
  if (av === undefined) {
210
217
  const perRO = isHeapEligible(ro);
@@ -218,6 +225,7 @@ export function compileHybridScene(
218
225
  return av;
219
226
  };
220
227
  const notElig = (ro: RenderObject): aval<boolean> => {
228
+ if (ro.heapAsserted === true) return notHeapEnabled;
221
229
  let av = notEligCache.get(ro);
222
230
  if (av === undefined) {
223
231
  const e = elig(ro);