@aardworx/wombat.rendering 0.19.6 → 0.19.8
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 +1 -0
- package/dist/resources/preparedRenderObject.d.ts.map +1 -1
- package/dist/resources/preparedRenderObject.js +7 -3
- package/dist/resources/preparedRenderObject.js.map +1 -1
- package/dist/runtime/heapAdapter.d.ts.map +1 -1
- package/dist/runtime/heapAdapter.js +14 -7
- package/dist/runtime/heapAdapter.js.map +1 -1
- package/dist/runtime/heapEffect.js +1 -1
- package/dist/runtime/heapEffect.js.map +1 -1
- package/dist/runtime/heapEligibility.d.ts.map +1 -1
- package/dist/runtime/heapEligibility.js +26 -19
- package/dist/runtime/heapEligibility.js.map +1 -1
- package/dist/runtime/heapIrBuilders.d.ts.map +1 -1
- package/dist/runtime/heapIrBuilders.js +7 -2
- package/dist/runtime/heapIrBuilders.js.map +1 -1
- package/dist/runtime/heapScene.d.ts +10 -1
- package/dist/runtime/heapScene.d.ts.map +1 -1
- package/dist/runtime/heapScene.js +55 -12
- package/dist/runtime/heapScene.js.map +1 -1
- package/package.json +1 -1
- package/src/resources/preparedRenderObject.ts +9 -4
- package/src/runtime/heapAdapter.ts +15 -7
- package/src/runtime/heapEffect.ts +1 -1
- package/src/runtime/heapEligibility.ts +20 -16
- package/src/runtime/heapIrBuilders.ts +12 -2
- package/src/runtime/heapScene.ts +33 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aardworx/wombat.rendering",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.8",
|
|
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",
|
|
@@ -215,11 +215,16 @@ function sampleTypeFor(type: Type): GPUTextureSampleType {
|
|
|
215
215
|
if (type.comparison === true) return "depth";
|
|
216
216
|
const s = type.sampled;
|
|
217
217
|
if (s.kind === "Int") return s.signed ? "sint" : "uint";
|
|
218
|
-
|
|
218
|
+
// Multisampled float textures can't be filtered — bind as unfilterable.
|
|
219
|
+
return type.multisampled ? "unfilterable-float" : "float";
|
|
219
220
|
}
|
|
220
221
|
return "float";
|
|
221
222
|
}
|
|
222
223
|
|
|
224
|
+
function isMultisampled(type: Type): boolean {
|
|
225
|
+
return type.kind === "Texture" && type.multisampled;
|
|
226
|
+
}
|
|
227
|
+
|
|
223
228
|
// ---------------------------------------------------------------------------
|
|
224
229
|
// Bind-group entry descriptions
|
|
225
230
|
// ---------------------------------------------------------------------------
|
|
@@ -227,7 +232,7 @@ function sampleTypeFor(type: Type): GPUTextureSampleType {
|
|
|
227
232
|
type EntryDesc =
|
|
228
233
|
| { kind: "ubuf"; binding: number; resource: AdaptiveResource<GPUBuffer> }
|
|
229
234
|
| { kind: "sbuf"; binding: number; resource: AdaptiveResource<GPUBuffer>; access: "read" | "read_write" }
|
|
230
|
-
| { kind: "tex"; binding: number; resource: AdaptiveResource<GPUTexture>; sampleType: GPUTextureSampleType }
|
|
235
|
+
| { kind: "tex"; binding: number; resource: AdaptiveResource<GPUTexture>; sampleType: GPUTextureSampleType; multisampled: boolean }
|
|
231
236
|
| { kind: "sampler"; binding: number; resource: AdaptiveResource<GPUSampler> };
|
|
232
237
|
|
|
233
238
|
interface GroupDesc {
|
|
@@ -253,7 +258,7 @@ function buildGroups(device: GPUDevice, descs: readonly EntryDesc[][]): GroupDes
|
|
|
253
258
|
visibility: e.access === "read_write" ? ShaderStage.FRAGMENT : visibility,
|
|
254
259
|
buffer: { type: e.access === "read_write" ? "storage" : "read-only-storage" },
|
|
255
260
|
};
|
|
256
|
-
case "tex": return { binding: e.binding, visibility, texture: { sampleType: e.sampleType } };
|
|
261
|
+
case "tex": return { binding: e.binding, visibility, texture: { sampleType: e.sampleType, ...(e.multisampled ? { multisampled: true } : {}) } };
|
|
257
262
|
case "sampler": return { binding: e.binding, visibility, sampler: { type: "filtering" } };
|
|
258
263
|
}
|
|
259
264
|
});
|
|
@@ -693,7 +698,7 @@ export function prepareRenderObject(
|
|
|
693
698
|
const res = prepareAdaptiveTexture(device, av, {
|
|
694
699
|
...(opts.label !== undefined ? { label: `${opts.label}.${t.name}` } : {}),
|
|
695
700
|
});
|
|
696
|
-
perGroup[t.group]!.push({ kind: "tex", binding: slotOf(t), resource: res, sampleType: sampleTypeFor(t.type) });
|
|
701
|
+
perGroup[t.group]!.push({ kind: "tex", binding: slotOf(t), resource: res, sampleType: sampleTypeFor(t.type), multisampled: isMultisampled(t.type) });
|
|
697
702
|
}
|
|
698
703
|
|
|
699
704
|
for (const s of iface.samplers) {
|
|
@@ -229,10 +229,10 @@ export function renderObjectToHeapSpec(
|
|
|
229
229
|
|
|
230
230
|
// 2. Indices: BufferView → aval<Uint32Array>. Map the underlying
|
|
231
231
|
// IBuffer aval; the heap path's IndexPool will key on this aval.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
232
|
+
// Non-indexed ROs (no `ro.indices`) carry no index aval — the spec
|
|
233
|
+
// then sets `vertexCount` and the megacall decodes the vertex directly.
|
|
234
|
+
const indices: aval<Uint32Array> | undefined =
|
|
235
|
+
ro.indices !== undefined ? indicesAvalFor(ro.indices.buffer) : undefined;
|
|
236
236
|
|
|
237
237
|
// 3. Texture/sampler: single-pair v1. The Sg compile layer binds
|
|
238
238
|
// each texture aval under both `name` and `${name}_view` so the
|
|
@@ -358,9 +358,17 @@ export function renderObjectToHeapSpec(
|
|
|
358
358
|
// 4. DrawCall: read once. Classifier validates fields are heap-
|
|
359
359
|
// compatible (indexed, instanceCount=1, zero offsets).
|
|
360
360
|
const dc = ro.drawCall.getValue(token);
|
|
361
|
-
if (dc.kind
|
|
362
|
-
throw new Error("heapAdapter:
|
|
361
|
+
if (dc.kind === "indexed" && indices === undefined) {
|
|
362
|
+
throw new Error("heapAdapter: indexed drawCall without indices");
|
|
363
|
+
}
|
|
364
|
+
if (dc.kind === "non-indexed" && indices !== undefined) {
|
|
365
|
+
throw new Error("heapAdapter: non-indexed drawCall with an index buffer");
|
|
363
366
|
}
|
|
367
|
+
// Indexed → carry the index aval; non-indexed → carry the vertex count and
|
|
368
|
+
// the megacall uses the local vertex index directly (no index lookup).
|
|
369
|
+
const geom = dc.kind === "indexed"
|
|
370
|
+
? { indices: indices! }
|
|
371
|
+
: { vertexCount: dc.vertexCount };
|
|
364
372
|
|
|
365
373
|
return {
|
|
366
374
|
effect: ro.effect,
|
|
@@ -368,7 +376,7 @@ export function renderObjectToHeapSpec(
|
|
|
368
376
|
inputs,
|
|
369
377
|
...(instanceAttributes !== undefined ? { instanceAttributes } : {}),
|
|
370
378
|
...(dc.instanceCount > 1 ? { instanceCount: dc.instanceCount } : {}),
|
|
371
|
-
|
|
379
|
+
...geom,
|
|
372
380
|
...(textures !== undefined ? { textures } : {}),
|
|
373
381
|
...(ro.modeRules !== undefined ? { modeRules: ro.modeRules } : {}),
|
|
374
382
|
// Pass `active` through unforced so the heap path can subscribe
|
|
@@ -839,5 +839,5 @@ export function megacallSearchPrelude(): string {
|
|
|
839
839
|
// to receive them as `u32` parameters — module-scope `var<private>`
|
|
840
840
|
// is rejected by some WGSL parsers (Safari/WebKit), so we thread the
|
|
841
841
|
// values through helper signatures instead.
|
|
842
|
-
return ` let _tileIdx = emitIdx >> 6u;\n var lo: u32 = firstDrawInTile[_tileIdx];\n var hi: u32 = firstDrawInTile[_tileIdx + 1u];\n loop {\n if (lo >= hi) { break; }\n let _mid = (lo + hi + 1u) >> 1u;\n if (drawTable[_mid * 5u] <= emitIdx) { lo = _mid; } else { hi = _mid - 1u; }\n }\n let _slot = lo;\n let _firstEmit = drawTable[_slot * 5u + 0u];\n let heap_drawIdx: u32 = drawTable[_slot * 5u + 1u];\n let _indexStart = drawTable[_slot * 5u + 2u];\n let _indexCount = drawTable[_slot * 5u + 3u];\n let _local = emitIdx - _firstEmit;\n let instId: u32 = _local / _indexCount;\n let vid: u32 = indexStorage[_indexStart +
|
|
842
|
+
return ` let _tileIdx = emitIdx >> 6u;\n var lo: u32 = firstDrawInTile[_tileIdx];\n var hi: u32 = firstDrawInTile[_tileIdx + 1u];\n loop {\n if (lo >= hi) { break; }\n let _mid = (lo + hi + 1u) >> 1u;\n if (drawTable[_mid * 5u] <= emitIdx) { lo = _mid; } else { hi = _mid - 1u; }\n }\n let _slot = lo;\n let _firstEmit = drawTable[_slot * 5u + 0u];\n let heap_drawIdx: u32 = drawTable[_slot * 5u + 1u];\n let _indexStart = drawTable[_slot * 5u + 2u];\n let _indexCount = drawTable[_slot * 5u + 3u];\n let _local = emitIdx - _firstEmit;\n let instId: u32 = _local / _indexCount;\n let _li: u32 = _local % _indexCount;\n var vid: u32 = _li;\n if (_indexStart != 0xffffffffu) { vid = indexStorage[_indexStart + _li]; }\n`;
|
|
843
843
|
}
|
|
@@ -229,29 +229,33 @@ export function isHeapEligible(ro: RenderObject): aval<boolean> {
|
|
|
229
229
|
for (const av of textures) if (!isHeapServableTexture(av.getValue(token))) return false;
|
|
230
230
|
for (const av of samplers) if (!isHeapServableSampler(av.getValue(token))) return false;
|
|
231
231
|
const dc = ro.drawCall.getValue(token);
|
|
232
|
-
if (dc.kind !== "indexed") return false;
|
|
233
232
|
if (dc.instanceCount < 1) return false;
|
|
234
|
-
if (dc.baseVertex !== 0) return false;
|
|
235
|
-
if (dc.firstIndex !== 0) return false;
|
|
236
233
|
if (dc.firstInstance !== 0) return false;
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
234
|
+
if (dc.kind === "indexed") {
|
|
235
|
+
if (ro.indices === undefined) return false;
|
|
236
|
+
if (dc.baseVertex !== 0) return false;
|
|
237
|
+
if (dc.firstIndex !== 0) return false;
|
|
238
|
+
// Whole-buffer wedge: the heap ingests the ENTIRE index + vertex
|
|
239
|
+
// buffers for an RO (IndexPool/AttributeArena copy `arr.length`,
|
|
240
|
+
// not the drawCall slice). An RO that consumes only a PREFIX of a
|
|
241
|
+
// shared index buffer (firstIndex=0 but indexCount < bufferCount —
|
|
242
|
+
// e.g. one glyph in a multi-glyph text run whose glyphs share a
|
|
243
|
+
// single atlas index buffer) would therefore drag the rest of the
|
|
244
|
+
// buffer into its draw: the trailing indices reference vertices
|
|
245
|
+
// past this RO's range, painting stray triangles. Until the heap
|
|
246
|
+
// honours the drawCall slice, only ROs that own their whole index
|
|
247
|
+
// buffer are eligible.
|
|
250
248
|
const ib = ro.indices.buffer.getValue(token);
|
|
251
249
|
if (ib.kind === "host") {
|
|
252
250
|
const idxBufCount = ib.sizeBytes >>> 2; // u32 indices
|
|
253
251
|
if (dc.indexCount !== idxBufCount) return false;
|
|
254
252
|
}
|
|
253
|
+
} else {
|
|
254
|
+
// Non-indexed: the megacall uses the local vertex index directly, so a
|
|
255
|
+
// prefix read is safe (vid = 0..vertexCount-1) — no whole-buffer wedge.
|
|
256
|
+
// firstVertex must be 0 (the heap maps vid → attribute[vid] from base 0).
|
|
257
|
+
if (ro.indices !== undefined) return false;
|
|
258
|
+
if (dc.firstVertex !== 0) return false;
|
|
255
259
|
}
|
|
256
260
|
return true;
|
|
257
261
|
});
|
|
@@ -354,11 +354,21 @@ export function buildMegacallPrelude(emitIdx: Expr): { stmts: Stmt[]; locals: Me
|
|
|
354
354
|
stmts.push({ kind: "Declare", var: localOff, init: { kind: "Expr", value: sub(emitIdx, varExpr(firstEmit), Tu32) } });
|
|
355
355
|
// let instId = _local / _indexCount;
|
|
356
356
|
stmts.push({ kind: "Declare", var: instId, init: { kind: "Expr", value: div(varExpr(localOff), varExpr(indexCount), Tu32) } });
|
|
357
|
-
//
|
|
357
|
+
// Non-indexed records carry _indexStart == 0xffffffff (HEAP_NONINDEXED): the
|
|
358
|
+
// vertex is the local index directly; otherwise decode through indexStorage.
|
|
359
|
+
// let vid = (_indexStart == 0xffffffff)
|
|
360
|
+
// ? (_local % _indexCount)
|
|
361
|
+
// : indexStorage[_indexStart + (_local % _indexCount)];
|
|
362
|
+
const liExpr = (): Expr => mod(varExpr(localOff), varExpr(indexCount), Tu32);
|
|
358
363
|
stmts.push({
|
|
359
364
|
kind: "Declare",
|
|
360
365
|
var: vid,
|
|
361
|
-
init: { kind: "Expr", value:
|
|
366
|
+
init: { kind: "Expr", value: select(
|
|
367
|
+
eqU32(varExpr(indexStart), constU32(0xffffffff)),
|
|
368
|
+
liExpr(),
|
|
369
|
+
item(indexStorage, add(varExpr(indexStart), liExpr(), Tu32), Tu32),
|
|
370
|
+
Tu32,
|
|
371
|
+
) },
|
|
362
372
|
});
|
|
363
373
|
|
|
364
374
|
return { stmts, locals: { heapDrawIdx, instId, vid } };
|
package/src/runtime/heapScene.ts
CHANGED
|
@@ -143,6 +143,11 @@ import {
|
|
|
143
143
|
* Geometry triple. Tightly-packed Float32 positions / normals (3
|
|
144
144
|
* floats per vertex) plus Uint32 indices.
|
|
145
145
|
*/
|
|
146
|
+
/** drawTable `firstIndex` sentinel marking a non-indexed record: the megacall
|
|
147
|
+
* prelude then uses the local vertex index directly (no indexStorage lookup).
|
|
148
|
+
* Must match the literal in heapEffect.ts's `megacallSearchPrelude`. */
|
|
149
|
+
export const HEAP_NONINDEXED = 0xffffffff;
|
|
150
|
+
|
|
146
151
|
export interface HeapGeometry {
|
|
147
152
|
readonly positions: Float32Array;
|
|
148
153
|
readonly normals: Float32Array;
|
|
@@ -453,8 +458,13 @@ export interface HeapDrawSpec {
|
|
|
453
458
|
/**
|
|
454
459
|
* Index buffer for this draw. Indices live in their own `INDEX`-
|
|
455
460
|
* usage `GPUBuffer` (WebGPU forces this), separate from the arena.
|
|
461
|
+
* Omit for a NON-INDEXED draw — then `vertexCount` is required and the
|
|
462
|
+
* megacall uses the local vertex index directly (no index-buffer lookup,
|
|
463
|
+
* no index-pool allocation). See the megacall prelude's `vid` select.
|
|
456
464
|
*/
|
|
457
|
-
readonly indices
|
|
465
|
+
readonly indices?: aval<Uint32Array> | Uint32Array;
|
|
466
|
+
/** Vertex count for a non-indexed draw (when `indices` is omitted). */
|
|
467
|
+
readonly vertexCount?: number;
|
|
458
468
|
/**
|
|
459
469
|
* Optional texture set. When present, adds a `texture_2d<f32>` at
|
|
460
470
|
* binding 4 and a sampler at binding 5; the FS must declare them.
|
|
@@ -3005,10 +3015,18 @@ export function buildHeapScene(
|
|
|
3005
3015
|
|
|
3006
3016
|
// Indices live in their own INDEX-usage buffer (WebGPU constraint).
|
|
3007
3017
|
// Aval-keyed: 19K instanced clones of the same mesh share one
|
|
3008
|
-
// index allocation + one upload.
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3018
|
+
// index allocation + one upload. NON-INDEXED draws (spec.indices
|
|
3019
|
+
// omitted) skip the index pool entirely: the drawTable record stores
|
|
3020
|
+
// firstIndex = HEAP_NONINDEXED (sentinel) and indexCount = vertexCount,
|
|
3021
|
+
// and the megacall prelude uses the local vertex index directly.
|
|
3022
|
+
const hasIndices = spec.indices !== undefined;
|
|
3023
|
+
const indicesAval = hasIndices ? (asAval(spec.indices!) as aval<Uint32Array>) : undefined;
|
|
3024
|
+
const idxAlloc = hasIndices
|
|
3025
|
+
? indexPool.acquire(device, arena.indices, indicesAval!, bucket.chunkIdx, readPlain(spec.indices!) as Uint32Array)
|
|
3026
|
+
: undefined;
|
|
3027
|
+
// Emit count per instance + the firstIndex field written to the record.
|
|
3028
|
+
const emitCount = hasIndices ? idxAlloc!.count : (spec.vertexCount ?? 0);
|
|
3029
|
+
const firstIndexField = hasIndices ? idxAlloc!.firstIndex : HEAP_NONINDEXED;
|
|
3012
3030
|
|
|
3013
3031
|
const localSlot = bucket.drawHeap.alloc();
|
|
3014
3032
|
// Per-RO instancing: read `spec.instanceCount` (defaults to 1).
|
|
@@ -3101,7 +3119,7 @@ export function buildHeapScene(
|
|
|
3101
3119
|
bucket.localPosRefs[localSlot] = perDrawRefs.get("Positions");
|
|
3102
3120
|
bucket.localNorRefs[localSlot] = perDrawRefs.get("Normals");
|
|
3103
3121
|
bucket.localEntries[localSlot] = {
|
|
3104
|
-
indexCount:
|
|
3122
|
+
indexCount: emitCount, firstIndex: firstIndexField, instanceCount,
|
|
3105
3123
|
};
|
|
3106
3124
|
bucket.localToDrawId[localSlot] = drawId;
|
|
3107
3125
|
bucket.drawSlots.add(localSlot);
|
|
@@ -3217,7 +3235,7 @@ export function buildHeapScene(
|
|
|
3217
3235
|
const uniformRefs: number[] = (bucket.uniformOrder ?? []).map(
|
|
3218
3236
|
name => perDrawRefs.get(name) ?? 0,
|
|
3219
3237
|
);
|
|
3220
|
-
partition.appendRecord(localSlot,
|
|
3238
|
+
partition.appendRecord(localSlot, firstIndexField, emitCount, instanceCount, roComboId, uniformRefs);
|
|
3221
3239
|
bucket.drawIdToRecord!.set(drawId, recIdx);
|
|
3222
3240
|
bucket.recordToDrawId![recIdx] = drawId;
|
|
3223
3241
|
bucket.drawIdToComboId!.set(drawId, roComboId);
|
|
@@ -3228,7 +3246,7 @@ export function buildHeapScene(
|
|
|
3228
3246
|
const numRecords = recIdx + 1;
|
|
3229
3247
|
const recBytes = numRecords * RECORD_BYTES;
|
|
3230
3248
|
const needBlocks = Math.max(1, Math.ceil(numRecords / SCAN_TILE_SIZE));
|
|
3231
|
-
const emitDelta =
|
|
3249
|
+
const emitDelta = emitCount * instanceCount;
|
|
3232
3250
|
for (const s of bucket.slots) {
|
|
3233
3251
|
s.drawTableBuf!.ensureCapacity(recBytes);
|
|
3234
3252
|
s.drawTableBuf!.setUsed(Math.max(s.drawTableBuf!.usedBytes, recBytes));
|
|
@@ -3261,15 +3279,15 @@ export function buildHeapScene(
|
|
|
3261
3279
|
// firstEmit is GPU-overwritten by the prefix-sum pass; 0 is fine.
|
|
3262
3280
|
shadow[recIdx * RECORD_U32 + 0] = 0;
|
|
3263
3281
|
shadow[recIdx * RECORD_U32 + 1] = localSlot;
|
|
3264
|
-
shadow[recIdx * RECORD_U32 + 2] =
|
|
3265
|
-
shadow[recIdx * RECORD_U32 + 3] =
|
|
3282
|
+
shadow[recIdx * RECORD_U32 + 2] = firstIndexField;
|
|
3283
|
+
shadow[recIdx * RECORD_U32 + 3] = emitCount;
|
|
3266
3284
|
shadow[recIdx * RECORD_U32 + 4] = instanceCount;
|
|
3267
3285
|
roSlot.recordCount = recIdx + 1;
|
|
3268
3286
|
roSlot.slotToRecord[localSlot] = recIdx;
|
|
3269
3287
|
roSlot.recordToSlot[recIdx] = localSlot;
|
|
3270
3288
|
if (byteOff < roSlot.drawTableDirtyMin) roSlot.drawTableDirtyMin = byteOff;
|
|
3271
3289
|
if (byteOff + RECORD_BYTES > roSlot.drawTableDirtyMax) roSlot.drawTableDirtyMax = byteOff + RECORD_BYTES;
|
|
3272
|
-
roSlot.totalEmitEstimate +=
|
|
3290
|
+
roSlot.totalEmitEstimate += emitCount * instanceCount;
|
|
3273
3291
|
const newNumTiles = Math.max(1, Math.ceil(roSlot.totalEmitEstimate / TILE_K));
|
|
3274
3292
|
roSlot.firstDrawInTileBuf!.ensureCapacity((newNumTiles + 1) * 4);
|
|
3275
3293
|
roSlot.scanDirty = true;
|
|
@@ -3286,7 +3304,7 @@ export function buildHeapScene(
|
|
|
3286
3304
|
// `subscribeActive` → `activeDirty` and drained in `update()`.
|
|
3287
3305
|
if (spec.active !== undefined) {
|
|
3288
3306
|
drawIdToActiveAval.set(drawId, spec.active);
|
|
3289
|
-
drawIdToOrigIndexCount.set(drawId,
|
|
3307
|
+
drawIdToOrigIndexCount.set(drawId, emitCount);
|
|
3290
3308
|
const initial = spec.active.getValue(outerTok);
|
|
3291
3309
|
subscribeActive(spec.active, drawId);
|
|
3292
3310
|
if (initial === false) {
|
|
@@ -5157,7 +5175,9 @@ export function buildHeapScene(
|
|
|
5157
5175
|
let _instCount = drawTable[_slot * 5u + 4u];
|
|
5158
5176
|
let _local = emitIdx - _firstEmit;
|
|
5159
5177
|
let _instId = _local / _indexCount;
|
|
5160
|
-
let
|
|
5178
|
+
let _li = _local % _indexCount;
|
|
5179
|
+
var _vid: u32 = _li;
|
|
5180
|
+
if (_indexStart != 0xffffffffu) { _vid = indexStorage[_indexStart + _li]; }
|
|
5161
5181
|
let base = i * 8u;
|
|
5162
5182
|
outRows[base + 0u] = _slot;
|
|
5163
5183
|
outRows[base + 1u] = _drawIdx;
|