@aardworx/wombat.rendering 0.19.1 → 0.19.3
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/resources/preparedRenderObject.d.ts +2 -1
- package/dist/resources/preparedRenderObject.d.ts.map +1 -1
- package/dist/resources/preparedRenderObject.js +11 -2
- package/dist/resources/preparedRenderObject.js.map +1 -1
- package/dist/runtime/heapEligibility.d.ts.map +1 -1
- package/dist/runtime/heapEligibility.js +20 -0
- package/dist/runtime/heapEligibility.js.map +1 -1
- package/dist/runtime/heapScene.d.ts.map +1 -1
- package/dist/runtime/heapScene.js +29 -5
- package/dist/runtime/heapScene.js.map +1 -1
- package/package.json +2 -2
- package/src/resources/preparedRenderObject.ts +8 -0
- package/src/runtime/heapEligibility.ts +19 -0
- package/src/runtime/heapScene.ts +29 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aardworx/wombat.rendering",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.3",
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
],
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@aardworx/wombat.adaptive": "^0.2.4",
|
|
59
|
-
"@aardworx/wombat.base": "^0.4.
|
|
59
|
+
"@aardworx/wombat.base": "^0.4.4",
|
|
60
60
|
"@aardworx/wombat.shader": "^0.5.9"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
@@ -357,6 +357,7 @@ export class PreparedRenderObject {
|
|
|
357
357
|
private readonly drawCall: aval<import("../core/index.js").DrawCall>,
|
|
358
358
|
pipelineCtx: PipelineBuildContext,
|
|
359
359
|
pipelineState: import("../core/index.js").PipelineState,
|
|
360
|
+
private readonly active: aval<boolean> | undefined = undefined,
|
|
360
361
|
) {
|
|
361
362
|
this.groups = groups;
|
|
362
363
|
this._pipelineCtx = pipelineCtx;
|
|
@@ -398,6 +399,12 @@ export class PreparedRenderObject {
|
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
record(pass: GPURenderPassEncoder, token: AdaptiveToken): void {
|
|
402
|
+
// Active gate: a reactive `RenderObject.active` flipping to false
|
|
403
|
+
// skips the draw entirely (legacy/standalone path's equivalent of
|
|
404
|
+
// the heap path's indexCount→0). Subscribed via getValue so the
|
|
405
|
+
// task re-records when it flips back. Without this, `<Sg Active=…>`
|
|
406
|
+
// is a no-op on the non-heap path and gated subtrees keep drawing.
|
|
407
|
+
if (this.active !== undefined && !this.active.getValue(token)) return;
|
|
401
408
|
// Resolve current pipeline. If `update` was already called by the
|
|
402
409
|
// walker this is a no-op cache hit; otherwise we still pick the
|
|
403
410
|
// right pipeline for the token-evaluated values.
|
|
@@ -741,6 +748,7 @@ export function prepareRenderObject(
|
|
|
741
748
|
obj.drawCall,
|
|
742
749
|
pipelineCtx,
|
|
743
750
|
obj.pipelineState,
|
|
751
|
+
obj.active,
|
|
744
752
|
);
|
|
745
753
|
}
|
|
746
754
|
|
|
@@ -234,6 +234,25 @@ export function isHeapEligible(ro: RenderObject): aval<boolean> {
|
|
|
234
234
|
if (dc.baseVertex !== 0) return false;
|
|
235
235
|
if (dc.firstIndex !== 0) return false;
|
|
236
236
|
if (dc.firstInstance !== 0) return false;
|
|
237
|
+
// Whole-buffer wedge: the heap ingests the ENTIRE index + vertex
|
|
238
|
+
// buffers for an RO (IndexPool/AttributeArena copy `arr.length`,
|
|
239
|
+
// not the drawCall slice). An RO that consumes only a PREFIX of a
|
|
240
|
+
// shared index buffer (firstIndex=0 but indexCount < bufferCount —
|
|
241
|
+
// e.g. one glyph in a multi-glyph text run whose glyphs share a
|
|
242
|
+
// single atlas index buffer) would therefore drag the rest of the
|
|
243
|
+
// buffer into its draw: the trailing indices reference vertices
|
|
244
|
+
// past this RO's range (including the next sub-mesh's / this
|
|
245
|
+
// glyph's band vertices), painting stray triangles. Until the heap
|
|
246
|
+
// honours the drawCall slice, only ROs that own their whole index
|
|
247
|
+
// buffer are eligible. Single-mesh ROs (indexCount === bufferCount)
|
|
248
|
+
// pass; shared-atlas slices fall to the legacy ScenePass.
|
|
249
|
+
if (ro.indices !== undefined) {
|
|
250
|
+
const ib = ro.indices.buffer.getValue(token);
|
|
251
|
+
if (ib.kind === "host") {
|
|
252
|
+
const idxBufCount = ib.sizeBytes >>> 2; // u32 indices
|
|
253
|
+
if (dc.indexCount !== idxBufCount) return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
237
256
|
return true;
|
|
238
257
|
});
|
|
239
258
|
}
|
package/src/runtime/heapScene.ts
CHANGED
|
@@ -2398,12 +2398,24 @@ export function buildHeapScene(
|
|
|
2398
2398
|
`has no captured descriptor — bucket promotion cannot synthesise a migration combo.`,
|
|
2399
2399
|
);
|
|
2400
2400
|
}
|
|
2401
|
+
// Read EFFECTIVE indexCount from the CPU slot's drawTable shadow
|
|
2402
|
+
// — for an active=false draw this is 0, so the new GPU master
|
|
2403
|
+
// record must carry the 0, not the original entry.indexCount.
|
|
2404
|
+
// Otherwise the CPU→GPU bucket promotion silently resurrects
|
|
2405
|
+
// every hidden draw.
|
|
2406
|
+
const cpuRecIdx = slot !== undefined ? slot.slotToRecord[ls] : undefined;
|
|
2407
|
+
const effIc = (cpuRecIdx !== undefined && cpuRecIdx >= 0 && slot !== undefined)
|
|
2408
|
+
? slot.drawTableShadow![cpuRecIdx * RECORD_U32 + 3]!
|
|
2409
|
+
: e.indexCount;
|
|
2410
|
+
const effInst = (cpuRecIdx !== undefined && cpuRecIdx >= 0 && slot !== undefined)
|
|
2411
|
+
? slot.drawTableShadow![cpuRecIdx * RECORD_U32 + 4]!
|
|
2412
|
+
: e.instanceCount;
|
|
2401
2413
|
cpuMigration.push({
|
|
2402
2414
|
localSlot: ls,
|
|
2403
2415
|
drawId,
|
|
2404
2416
|
firstIndex: e.firstIndex,
|
|
2405
|
-
indexCount:
|
|
2406
|
-
instanceCount:
|
|
2417
|
+
indexCount: effIc,
|
|
2418
|
+
instanceCount: effInst,
|
|
2407
2419
|
perDrawRefs: bucket.localPerDrawRefs[ls]!,
|
|
2408
2420
|
descriptor,
|
|
2409
2421
|
targetComboId: 0,
|
|
@@ -3682,10 +3694,17 @@ export function buildHeapScene(
|
|
|
3682
3694
|
// Swap-pop from old slot's drawTable.
|
|
3683
3695
|
const oldSlot = bucket.slots[oldSlotIdx]!;
|
|
3684
3696
|
const entry = bucket.localEntries[localSlot];
|
|
3685
|
-
const emit = entry !== undefined ? entry.indexCount * entry.instanceCount : 0;
|
|
3686
3697
|
const oldRecIdx = oldSlot.slotToRecord[localSlot]!;
|
|
3687
3698
|
const lastRecIdx = oldSlot.recordCount - 1;
|
|
3688
3699
|
const oldShadow = oldSlot.drawTableShadow!;
|
|
3700
|
+
// Read the EFFECTIVE indexCount from the old slot's drawTable
|
|
3701
|
+
// shadow — for an active=false draw this is already 0, so the
|
|
3702
|
+
// emit accounting on the OLD slot must NOT subtract the
|
|
3703
|
+
// original (entry.indexCount) which would double-decrement and
|
|
3704
|
+
// also pin the carried-over count below.
|
|
3705
|
+
const effectiveIndexCount = oldShadow[oldRecIdx * RECORD_U32 + 3]!;
|
|
3706
|
+
const effectiveInstanceCount = oldShadow[oldRecIdx * RECORD_U32 + 4]!;
|
|
3707
|
+
const emit = effectiveIndexCount * effectiveInstanceCount;
|
|
3689
3708
|
if (oldRecIdx !== lastRecIdx) {
|
|
3690
3709
|
const dst = oldRecIdx * RECORD_U32;
|
|
3691
3710
|
const src = lastRecIdx * RECORD_U32;
|
|
@@ -3720,8 +3739,13 @@ export function buildHeapScene(
|
|
|
3720
3739
|
newShadow[newRecIdx * RECORD_U32 + 0] = 0;
|
|
3721
3740
|
newShadow[newRecIdx * RECORD_U32 + 1] = localSlot;
|
|
3722
3741
|
newShadow[newRecIdx * RECORD_U32 + 2] = entry !== undefined ? entry.firstIndex : 0;
|
|
3723
|
-
|
|
3724
|
-
|
|
3742
|
+
// Carry over the EFFECTIVE indexCount (read from oldShadow
|
|
3743
|
+
// above) so an active=false draw remains hidden after the
|
|
3744
|
+
// reslot. Using entry.indexCount here was the bug: it always
|
|
3745
|
+
// wrote the ORIGINAL count, resurrecting hidden draws every
|
|
3746
|
+
// time a mode-key flip moved them between slots.
|
|
3747
|
+
newShadow[newRecIdx * RECORD_U32 + 3] = effectiveIndexCount;
|
|
3748
|
+
newShadow[newRecIdx * RECORD_U32 + 4] = effectiveInstanceCount;
|
|
3725
3749
|
newSlot.recordCount = newRecIdx + 1;
|
|
3726
3750
|
newSlot.slotToRecord[localSlot] = newRecIdx;
|
|
3727
3751
|
newSlot.recordToSlot[newRecIdx] = localSlot;
|