@forgeax/engine-rhi-debug 0.1.28 → 0.1.29
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/README.md +13 -0
- package/dist/__tests__/query-set-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/query-set-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/query-set-replay-fixture.d.ts +39 -0
- package/dist/__tests__/query-set-replay-fixture.d.ts.map +1 -0
- package/dist/__tests__/recorder-canvas-context.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-canvas-context.unit.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +465 -53
- package/dist/index.mjs.map +1 -1
- package/dist/protocol/event-semantics.d.ts +1 -1
- package/dist/protocol/event-semantics.d.ts.map +1 -1
- package/dist/protocol/types.d.ts +1 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/recorder/closure.d.ts.map +1 -1
- package/dist/recorder/core.d.ts +10 -1
- package/dist/recorder/core.d.ts.map +1 -1
- package/dist/recorder/device.d.ts.map +1 -1
- package/dist/recorder/encoder.d.ts.map +1 -1
- package/dist/recorder/pass.d.ts.map +1 -1
- package/dist/recorder/pipeline.d.ts +5 -0
- package/dist/recorder/pipeline.d.ts.map +1 -0
- package/dist/recorder/proxy.d.ts +3 -1
- package/dist/recorder/proxy.d.ts.map +1 -1
- package/dist/recorder/session.d.ts +1 -1
- package/dist/recorder/session.d.ts.map +1 -1
- package/dist/recorder/shader.d.ts +3 -1
- package/dist/recorder/shader.d.ts.map +1 -1
- package/dist/recorder.d.ts +2 -2
- package/dist/recorder.d.ts.map +1 -1
- package/dist/replay/execute.d.ts.map +1 -1
- package/dist/replay/resources.d.ts +4 -1
- package/dist/replay/resources.d.ts.map +1 -1
- package/dist/replay/session.d.ts +7 -0
- package/dist/replay/session.d.ts.map +1 -1
- package/dist/types.d.ts +39 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/coverage-invariant.test-d.ts +4 -5
- package/src/__tests__/coverage-invariant.unit.test.ts +6 -17
- package/src/__tests__/e2e.browser.test.ts +50 -0
- package/src/__tests__/guard-gates.test.ts +1 -1
- package/src/__tests__/query-set-lifecycle.unit.test.ts +265 -0
- package/src/__tests__/query-set-replay-fixture.ts +553 -0
- package/src/__tests__/readback-format-matrix.dawn.test.ts +7 -0
- package/src/__tests__/recorder-canvas-context.unit.test.ts +67 -0
- package/src/__tests__/recorder-session.integration.test.ts +55 -0
- package/src/__tests__/recorder-session.unit.test.ts +18 -1
- package/src/__tests__/replay-session.dawn.test.ts +49 -0
- package/src/__tests__/replay-session.test-d.ts +1 -0
- package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +61 -0
- package/src/index.ts +1 -0
- package/src/protocol/event-semantics.ts +20 -2
- package/src/protocol/types.ts +1 -0
- package/src/protocol/validation.ts +31 -0
- package/src/recorder/assemble.ts +8 -1
- package/src/recorder/closure.ts +17 -0
- package/src/recorder/core.ts +11 -0
- package/src/recorder/device.ts +33 -6
- package/src/recorder/encoder.ts +22 -4
- package/src/recorder/pass.ts +7 -2
- package/src/recorder/pipeline.ts +31 -0
- package/src/recorder/proxy.ts +58 -2
- package/src/recorder/session.ts +1 -1
- package/src/recorder/shader.ts +29 -22
- package/src/recorder.ts +6 -2
- package/src/replay/execute.ts +111 -0
- package/src/replay/readback.ts +8 -3
- package/src/replay/resources.ts +6 -0
- package/src/replay/session.ts +234 -25
- package/src/types.ts +53 -12
package/src/replay/session.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
MappedBuffer,
|
|
2
3
|
RhiCommandEncoder,
|
|
3
4
|
RhiComputePassEncoder,
|
|
4
5
|
RhiDevice,
|
|
@@ -69,6 +70,18 @@ export interface ReplaySession {
|
|
|
69
70
|
subresource?: ReadbackSubresource,
|
|
70
71
|
signal?: AbortSignal,
|
|
71
72
|
): Promise<Result<ReplayReadbackResult, RhiDebugError>>;
|
|
73
|
+
/**
|
|
74
|
+
* Replay the captured stream through one indexed work item, then read the
|
|
75
|
+
* resource from that exact post-work state. Unlike readResource(), this is
|
|
76
|
+
* not a bootstrap-only read and therefore can inspect compute/storage
|
|
77
|
+
* outputs produced by the selected work without creating a second tape.
|
|
78
|
+
*/
|
|
79
|
+
readResourceAtWork(
|
|
80
|
+
resourceId: string,
|
|
81
|
+
workIndex: number,
|
|
82
|
+
subresource?: ReadbackSubresource,
|
|
83
|
+
signal?: AbortSignal,
|
|
84
|
+
): Promise<Result<ReplayReadbackResult, RhiDebugError>>;
|
|
72
85
|
dispose(): Promise<Result<void, RhiDebugError>>;
|
|
73
86
|
}
|
|
74
87
|
|
|
@@ -204,6 +217,42 @@ export async function openReplay(
|
|
|
204
217
|
backend.createShaderModule,
|
|
205
218
|
);
|
|
206
219
|
},
|
|
220
|
+
async readResourceAtWork(resourceId, workIndex, subresource, signal) {
|
|
221
|
+
if (disposed) return positionError(workIndex, index.works.length);
|
|
222
|
+
if (signal?.aborted) {
|
|
223
|
+
return err(
|
|
224
|
+
createRhiDebugError('readback-failed', {
|
|
225
|
+
stage: 'readback',
|
|
226
|
+
cause: 'readback was aborted',
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const work = index.works[workIndex];
|
|
231
|
+
if (work === undefined || model.works[workIndex] === undefined) {
|
|
232
|
+
return positionError(workIndex, index.works.length);
|
|
233
|
+
}
|
|
234
|
+
const cleared = await reset();
|
|
235
|
+
if (!cleared.ok) return cleared;
|
|
236
|
+
const bootstrapped = await prepare();
|
|
237
|
+
if (!bootstrapped.ok) return bootstrapped;
|
|
238
|
+
const replayed = await replayThroughWork(context, index, work, signal);
|
|
239
|
+
if (!replayed.ok) return replayed;
|
|
240
|
+
const read = await readReplayResource(
|
|
241
|
+
backend.device,
|
|
242
|
+
table,
|
|
243
|
+
resourceId,
|
|
244
|
+
subresource,
|
|
245
|
+
backend.createShaderModule,
|
|
246
|
+
);
|
|
247
|
+
if (!read.ok) return read;
|
|
248
|
+
return ok({
|
|
249
|
+
...read.value,
|
|
250
|
+
provenance: {
|
|
251
|
+
...read.value.provenance,
|
|
252
|
+
selectedWorkIndex: work.workIndex,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
},
|
|
207
256
|
async dispose() {
|
|
208
257
|
if (disposed) return ok(undefined);
|
|
209
258
|
const result = table.dispose();
|
|
@@ -328,7 +377,14 @@ async function executeBootstrapResource(
|
|
|
328
377
|
bootstrapIndex: number,
|
|
329
378
|
): Promise<Result<void, RhiDebugError>> {
|
|
330
379
|
const event: RhiCallEvent = JSON.parse(JSON.stringify(resource.create));
|
|
331
|
-
|
|
380
|
+
// Captured GPU-written buffers need not allow COPY_DST. Restore their
|
|
381
|
+
// initial bytes through creation mapping, which is legal for every usage
|
|
382
|
+
// (including MAP_WRITE, where adding COPY_DST would itself be invalid).
|
|
383
|
+
const replayEvent =
|
|
384
|
+
event.kind === 'createBuffer' && resource.initialData.length > 0
|
|
385
|
+
? { ...event, desc: { ...event.desc, mappedAtCreation: true } }
|
|
386
|
+
: event;
|
|
387
|
+
const result = await executeEvent(context, replayEvent, -bootstrapIndex - 1);
|
|
332
388
|
return result;
|
|
333
389
|
}
|
|
334
390
|
|
|
@@ -351,25 +407,50 @@ async function seedBootstrapResource(
|
|
|
351
407
|
'bootstrap initialData requires a buffer or color texture resource',
|
|
352
408
|
);
|
|
353
409
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
410
|
+
// executeBootstrapResource created this buffer mapped specifically for
|
|
411
|
+
// restoration. It must be unmapped before any recorded work can consume it.
|
|
412
|
+
const mapped = entry.resource.value as MappedBuffer;
|
|
413
|
+
// RhiNull deliberately exposes opaque structural handles rather than a
|
|
414
|
+
// CPU-visible mapping implementation. Its replay bytes have no observable
|
|
415
|
+
// storage, so there is nothing to seed; real backends always provide the
|
|
416
|
+
// MappedBuffer methods below and still take the strict mapping path.
|
|
417
|
+
if (
|
|
418
|
+
typeof (mapped as Partial<MappedBuffer>).getMappedRange !== 'function' ||
|
|
419
|
+
typeof (mapped as Partial<MappedBuffer>).unmap !== 'function'
|
|
420
|
+
) {
|
|
421
|
+
if (context.device.caps.backendKind === 'null') return ok(undefined);
|
|
422
|
+
return eventFailure(
|
|
423
|
+
-bootstrapIndex - 1,
|
|
424
|
+
event,
|
|
425
|
+
'write',
|
|
426
|
+
'bootstrap buffer mapping is unavailable on the replay backend',
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
try {
|
|
430
|
+
const range = mapped.getMappedRange();
|
|
431
|
+
if (!range.ok) return eventFailure(-bootstrapIndex - 1, event, 'write', range.error);
|
|
432
|
+
const target = new Uint8Array(range.value);
|
|
433
|
+
for (const slice of resource.initialData) {
|
|
434
|
+
const blob = context.tape.blobs.find((candidate) => candidate.hash === slice.hash);
|
|
435
|
+
if (blob === undefined) {
|
|
436
|
+
return eventFailure(-bootstrapIndex - 1, event, 'lookup', `blob ${slice.hash} is missing`);
|
|
437
|
+
}
|
|
438
|
+
const end = slice.byteOffset + slice.byteLength;
|
|
439
|
+
if (slice.byteOffset < 0 || slice.byteLength < 0 || end > blob.bytes.byteLength) {
|
|
440
|
+
return eventFailure(
|
|
441
|
+
-bootstrapIndex - 1,
|
|
442
|
+
event,
|
|
443
|
+
'lookup',
|
|
444
|
+
`blob ${slice.hash} does not contain initialData slice [${slice.byteOffset}, ${end})`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
if (slice.byteLength > target.byteLength) {
|
|
448
|
+
return eventFailure(-bootstrapIndex - 1, event, 'write', 'initialData exceeds buffer size');
|
|
449
|
+
}
|
|
450
|
+
target.set(blob.bytes.subarray(slice.byteOffset, end));
|
|
372
451
|
}
|
|
452
|
+
} finally {
|
|
453
|
+
mapped.unmap();
|
|
373
454
|
}
|
|
374
455
|
return ok(undefined);
|
|
375
456
|
}
|
|
@@ -489,12 +570,8 @@ async function finalizeWorkPass(
|
|
|
489
570
|
`pass ${begin.passHandleId} is not open`,
|
|
490
571
|
);
|
|
491
572
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
} catch (cause) {
|
|
495
|
-
return eventFailure(pass.endEventIndex ?? work.eventIndex, begin, 'encode', cause);
|
|
496
|
-
}
|
|
497
|
-
context.table.delete(begin.passHandleId);
|
|
573
|
+
const closed = closeReplayPass(context, entry, begin, pass, work.eventIndex);
|
|
574
|
+
if (!closed.ok) return closed;
|
|
498
575
|
const finishEventIndex = findNextEvent(
|
|
499
576
|
context.tape.events,
|
|
500
577
|
pass.beginEventIndex,
|
|
@@ -510,6 +587,13 @@ async function finalizeWorkPass(
|
|
|
510
587
|
`encoder ${begin.cmdHandleId} is not available`,
|
|
511
588
|
);
|
|
512
589
|
}
|
|
590
|
+
const closure = await replayCommandClosure(
|
|
591
|
+
context,
|
|
592
|
+
begin.cmdHandleId,
|
|
593
|
+
(pass.endEventIndex ?? work.eventIndex) + 1,
|
|
594
|
+
finishEventIndex ?? context.tape.events.length,
|
|
595
|
+
);
|
|
596
|
+
if (!closure.ok) return closure;
|
|
513
597
|
const finished = (encoder.resource.value as RhiCommandEncoder).finish();
|
|
514
598
|
const finishEvent = context.tape.events[finishEventIndex ?? work.eventIndex] ?? begin;
|
|
515
599
|
if (!finished.ok)
|
|
@@ -538,6 +622,131 @@ async function finalizeWorkPass(
|
|
|
538
622
|
return ok(undefined);
|
|
539
623
|
}
|
|
540
624
|
|
|
625
|
+
function closeReplayPass(
|
|
626
|
+
context: ReplayExecutionContext,
|
|
627
|
+
entry: NonNullable<ReturnType<ReplayExecutionContext['table']['get']>>,
|
|
628
|
+
begin: Extract<RhiCallEvent, { kind: 'beginRenderPass' | 'beginComputePass' }>,
|
|
629
|
+
pass: TapeIndex['passes'][number],
|
|
630
|
+
selectedEventIndex: number,
|
|
631
|
+
): Result<void, RhiDebugError> {
|
|
632
|
+
const activeQuery = activeOcclusionQuery(
|
|
633
|
+
context.tape.events,
|
|
634
|
+
begin.kind === 'beginRenderPass' ? begin.passHandleId : undefined,
|
|
635
|
+
pass.beginEventIndex,
|
|
636
|
+
selectedEventIndex,
|
|
637
|
+
);
|
|
638
|
+
if (!activeQuery.ok) return activeQuery;
|
|
639
|
+
try {
|
|
640
|
+
if (activeQuery.value !== undefined) {
|
|
641
|
+
if (entry.resource.role !== 'render-pass') {
|
|
642
|
+
return eventFailure(
|
|
643
|
+
activeQuery.value.eventIndex,
|
|
644
|
+
activeQuery.value.event,
|
|
645
|
+
'lookup',
|
|
646
|
+
'an active occlusion query belongs to a non-render pass',
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
const endedQuery = (entry.resource.value as RhiRenderPassEncoder).endOcclusionQuery();
|
|
650
|
+
if (!endedQuery.ok) {
|
|
651
|
+
return eventFailure(
|
|
652
|
+
activeQuery.value.eventIndex,
|
|
653
|
+
activeQuery.value.event,
|
|
654
|
+
'encode',
|
|
655
|
+
endedQuery.error,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
(entry.resource.value as RhiRenderPassEncoder | RhiComputePassEncoder).end();
|
|
660
|
+
} catch (cause) {
|
|
661
|
+
return eventFailure(pass.endEventIndex ?? selectedEventIndex, begin, 'encode', cause);
|
|
662
|
+
}
|
|
663
|
+
context.table.delete(begin.passHandleId);
|
|
664
|
+
return ok(undefined);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
async function replayCommandClosure(
|
|
668
|
+
context: ReplayExecutionContext,
|
|
669
|
+
cmdHandleId: string,
|
|
670
|
+
start: number,
|
|
671
|
+
end: number,
|
|
672
|
+
): Promise<Result<void, RhiDebugError>> {
|
|
673
|
+
for (let eventIndex = start; eventIndex < end; eventIndex++) {
|
|
674
|
+
const event = context.tape.events[eventIndex];
|
|
675
|
+
if (event === undefined || !isCommandClosureEvent(event)) continue;
|
|
676
|
+
if (event.cmdHandleId !== cmdHandleId) continue;
|
|
677
|
+
const result = await executeEvent(context, event, eventIndex);
|
|
678
|
+
if (!result.ok) return result;
|
|
679
|
+
}
|
|
680
|
+
return ok(undefined);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
type ActiveOcclusionQuery = Extract<RhiCallEvent, { kind: 'beginOcclusionQuery' }>;
|
|
684
|
+
|
|
685
|
+
function activeOcclusionQuery(
|
|
686
|
+
events: readonly RhiCallEvent[],
|
|
687
|
+
passHandleId: string | undefined,
|
|
688
|
+
beginEventIndex: number,
|
|
689
|
+
selectedEventIndex: number,
|
|
690
|
+
): Result<
|
|
691
|
+
{ readonly eventIndex: number; readonly event: ActiveOcclusionQuery } | undefined,
|
|
692
|
+
RhiDebugError
|
|
693
|
+
> {
|
|
694
|
+
if (passHandleId === undefined) return ok(undefined);
|
|
695
|
+
let active: { readonly eventIndex: number; readonly event: ActiveOcclusionQuery } | undefined;
|
|
696
|
+
for (let eventIndex = beginEventIndex + 1; eventIndex <= selectedEventIndex; eventIndex++) {
|
|
697
|
+
const event = events[eventIndex];
|
|
698
|
+
if (event === undefined || !('passHandleId' in event) || event.passHandleId !== passHandleId)
|
|
699
|
+
continue;
|
|
700
|
+
if (event.kind === 'beginOcclusionQuery') {
|
|
701
|
+
if (active !== undefined) {
|
|
702
|
+
return eventFailure(
|
|
703
|
+
eventIndex,
|
|
704
|
+
event,
|
|
705
|
+
'encode',
|
|
706
|
+
'an occlusion query is already active for this render pass',
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
active = { eventIndex, event };
|
|
710
|
+
} else if (event.kind === 'endOcclusionQuery') {
|
|
711
|
+
if (active === undefined) {
|
|
712
|
+
return eventFailure(
|
|
713
|
+
eventIndex,
|
|
714
|
+
event,
|
|
715
|
+
'encode',
|
|
716
|
+
'an occlusion query ended without a matching begin',
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
active = undefined;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return ok(active);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function isCommandClosureEvent(event: RhiCallEvent): event is Extract<
|
|
726
|
+
RhiCallEvent,
|
|
727
|
+
{
|
|
728
|
+
kind:
|
|
729
|
+
| 'resolveQuerySet'
|
|
730
|
+
| 'copyBufferToBuffer'
|
|
731
|
+
| 'copyBufferToTexture'
|
|
732
|
+
| 'copyTextureToBuffer'
|
|
733
|
+
| 'copyTextureToTexture'
|
|
734
|
+
| 'clearBuffer';
|
|
735
|
+
}
|
|
736
|
+
> {
|
|
737
|
+
switch (event.kind) {
|
|
738
|
+
case 'resolveQuerySet':
|
|
739
|
+
case 'copyBufferToBuffer':
|
|
740
|
+
case 'copyBufferToTexture':
|
|
741
|
+
case 'copyTextureToBuffer':
|
|
742
|
+
case 'copyTextureToTexture':
|
|
743
|
+
case 'clearBuffer':
|
|
744
|
+
return true;
|
|
745
|
+
default:
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
541
750
|
function findNextEvent(
|
|
542
751
|
events: readonly RhiCallEvent[],
|
|
543
752
|
start: number,
|
package/src/types.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Shape:
|
|
4
4
|
// - RhiCallEvent: closed union (~40 kind incl. initialData + frameMark), each kind name 1:1 with RHI method name.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// Excludes: resolveQuerySet (per OOS-3).
|
|
5
|
+
// Query-set lifecycle and occlusion resolve calls are ordinary events;
|
|
6
|
+
// timestamp writes and render-bundle execution remain deferred.
|
|
8
7
|
// Includes: core RHI + copyExternalImageToTexture + clearBuffer (per IS-11).
|
|
9
8
|
// - Tape: header with formatVersion, rhiCapsRecorded, events array, blobPool map.
|
|
10
9
|
// - InspectReport: frameIdx, workIndex, bindings, drawCall, rt (path string, not inline base64).
|
|
@@ -100,6 +99,17 @@ export interface RhiCallEventDestroyTexture {
|
|
|
100
99
|
readonly handleId: HandleId;
|
|
101
100
|
}
|
|
102
101
|
|
|
102
|
+
export interface RhiCallEventCreateQuerySet {
|
|
103
|
+
readonly kind: 'createQuerySet';
|
|
104
|
+
readonly handleId: HandleId;
|
|
105
|
+
readonly desc: Pick<GPUQuerySetDescriptor, 'label' | 'type' | 'count'>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface RhiCallEventDestroyQuerySet {
|
|
109
|
+
readonly kind: 'destroyQuerySet';
|
|
110
|
+
readonly handleId: HandleId;
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
export interface RhiCallEventCreateTextureView {
|
|
104
114
|
readonly kind: 'createTextureView';
|
|
105
115
|
readonly sourceHandleId: HandleId;
|
|
@@ -122,6 +132,13 @@ export interface RhiCallEventCreateSampler {
|
|
|
122
132
|
readonly desc?: Partial<GPUSamplerDescriptor> | undefined;
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
export interface RhiCallEventGetBindGroupLayout {
|
|
136
|
+
readonly kind: 'getBindGroupLayout';
|
|
137
|
+
readonly handleId: HandleId;
|
|
138
|
+
readonly pipelineHandleId: HandleId;
|
|
139
|
+
readonly index: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
export interface RhiCallEventCreateBindGroupLayout {
|
|
126
143
|
readonly kind: 'createBindGroupLayout';
|
|
127
144
|
readonly handleId: HandleId;
|
|
@@ -289,7 +306,7 @@ export interface RhiCallEventBeginRenderPass {
|
|
|
289
306
|
readonly [key: string]: unknown;
|
|
290
307
|
}
|
|
291
308
|
| undefined;
|
|
292
|
-
|
|
309
|
+
/** The opaque query set is represented by the parallel handle id below. */
|
|
293
310
|
readonly timestampWrites?: unknown | undefined;
|
|
294
311
|
readonly maxDrawCount?: number | undefined;
|
|
295
312
|
};
|
|
@@ -301,6 +318,7 @@ export interface RhiCallEventBeginRenderPass {
|
|
|
301
318
|
*/
|
|
302
319
|
readonly colorAttachmentResolveTargetHandleIds?: readonly (HandleId | undefined)[];
|
|
303
320
|
readonly depthStencilViewHandleId?: HandleId | undefined;
|
|
321
|
+
readonly occlusionQuerySetHandleId?: HandleId | undefined;
|
|
304
322
|
}
|
|
305
323
|
|
|
306
324
|
export interface RhiCallEventBeginComputePass {
|
|
@@ -362,6 +380,16 @@ export interface RhiCallEventClearBuffer {
|
|
|
362
380
|
readonly size?: number | undefined;
|
|
363
381
|
}
|
|
364
382
|
|
|
383
|
+
export interface RhiCallEventResolveQuerySet {
|
|
384
|
+
readonly kind: 'resolveQuerySet';
|
|
385
|
+
readonly cmdHandleId: HandleId;
|
|
386
|
+
readonly querySetHandleId: HandleId;
|
|
387
|
+
readonly firstQuery: number;
|
|
388
|
+
readonly queryCount: number;
|
|
389
|
+
readonly destinationHandleId: HandleId;
|
|
390
|
+
readonly destinationOffset: number;
|
|
391
|
+
}
|
|
392
|
+
|
|
365
393
|
export interface RhiCallEventPushDebugGroup {
|
|
366
394
|
readonly kind: 'pushDebugGroup';
|
|
367
395
|
readonly cmdHandleId: HandleId;
|
|
@@ -468,6 +496,17 @@ export interface RhiCallEventEndRenderPass {
|
|
|
468
496
|
readonly passHandleId: HandleId;
|
|
469
497
|
}
|
|
470
498
|
|
|
499
|
+
export interface RhiCallEventBeginOcclusionQuery {
|
|
500
|
+
readonly kind: 'beginOcclusionQuery';
|
|
501
|
+
readonly passHandleId: HandleId;
|
|
502
|
+
readonly queryIndex: number;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export interface RhiCallEventEndOcclusionQuery {
|
|
506
|
+
readonly kind: 'endOcclusionQuery';
|
|
507
|
+
readonly passHandleId: HandleId;
|
|
508
|
+
}
|
|
509
|
+
|
|
471
510
|
export interface RhiCallEventSetBlendConstant {
|
|
472
511
|
readonly kind: 'setBlendConstant';
|
|
473
512
|
readonly passHandleId: HandleId;
|
|
@@ -555,8 +594,9 @@ export interface RhiCallEventInitialData {
|
|
|
555
594
|
* Closed union of all recordable RHI call events.
|
|
556
595
|
*
|
|
557
596
|
* v1 covers: core RHI methods + copyExternalImageToTexture + clearBuffer (IS-11).
|
|
558
|
-
*
|
|
559
|
-
*
|
|
597
|
+
* Timestamp writes and render-bundle execution remain deferred; query-set
|
|
598
|
+
* lifecycle, resolve, and occlusion-query events are represented as ordinary
|
|
599
|
+
* events.
|
|
560
600
|
*
|
|
561
601
|
* Kinds are named 1:1 with RHI method names per plan-strategy §8 naming convention.
|
|
562
602
|
* The frameMark kind is the only non-method event — it marks frame boundaries.
|
|
@@ -567,9 +607,12 @@ export type RhiCallEvent =
|
|
|
567
607
|
| RhiCallEventCreateTexture
|
|
568
608
|
| RhiCallEventDestroyBuffer
|
|
569
609
|
| RhiCallEventDestroyTexture
|
|
610
|
+
| RhiCallEventCreateQuerySet
|
|
611
|
+
| RhiCallEventDestroyQuerySet
|
|
570
612
|
| RhiCallEventCreateTextureView
|
|
571
613
|
| RhiCallEventCreateSampler
|
|
572
614
|
| RhiCallEventCreateBindGroupLayout
|
|
615
|
+
| RhiCallEventGetBindGroupLayout
|
|
573
616
|
| RhiCallEventCreateBindGroup
|
|
574
617
|
| RhiCallEventCreatePipelineLayout
|
|
575
618
|
| RhiCallEventCreateRenderPipeline
|
|
@@ -587,6 +630,7 @@ export type RhiCallEvent =
|
|
|
587
630
|
| RhiCallEventCopyTextureToBuffer
|
|
588
631
|
| RhiCallEventCopyTextureToTexture
|
|
589
632
|
| RhiCallEventClearBuffer
|
|
633
|
+
| RhiCallEventResolveQuerySet
|
|
590
634
|
| RhiCallEventPushDebugGroup
|
|
591
635
|
| RhiCallEventPopDebugGroup
|
|
592
636
|
| RhiCallEventInsertDebugMarker
|
|
@@ -601,6 +645,8 @@ export type RhiCallEvent =
|
|
|
601
645
|
| RhiCallEventSetScissorRect
|
|
602
646
|
| RhiCallEventSetStencilReference
|
|
603
647
|
| RhiCallEventEndRenderPass
|
|
648
|
+
| RhiCallEventBeginOcclusionQuery
|
|
649
|
+
| RhiCallEventEndOcclusionQuery
|
|
604
650
|
| RhiCallEventSetBlendConstant
|
|
605
651
|
| RhiCallEventDrawIndirect
|
|
606
652
|
| RhiCallEventDrawIndexedIndirect
|
|
@@ -881,9 +927,4 @@ export type InspectFields = 'bindings' | 'drawCall' | 'rt';
|
|
|
881
927
|
*
|
|
882
928
|
* @internal
|
|
883
929
|
*/
|
|
884
|
-
export const DEFERRED_COMMANDS = new Set<string>([
|
|
885
|
-
'beginOcclusionQuery',
|
|
886
|
-
'endOcclusionQuery',
|
|
887
|
-
'executeBundles',
|
|
888
|
-
'resolveQuerySet',
|
|
889
|
-
]);
|
|
930
|
+
export const DEFERRED_COMMANDS = new Set<string>(['executeBundles', 'writeTimestamp']);
|