@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.
Files changed (72) hide show
  1. package/README.md +13 -0
  2. package/dist/__tests__/query-set-lifecycle.unit.test.d.ts +2 -0
  3. package/dist/__tests__/query-set-lifecycle.unit.test.d.ts.map +1 -0
  4. package/dist/__tests__/query-set-replay-fixture.d.ts +39 -0
  5. package/dist/__tests__/query-set-replay-fixture.d.ts.map +1 -0
  6. package/dist/__tests__/recorder-canvas-context.unit.test.d.ts +2 -0
  7. package/dist/__tests__/recorder-canvas-context.unit.test.d.ts.map +1 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.mjs +465 -53
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/protocol/event-semantics.d.ts +1 -1
  13. package/dist/protocol/event-semantics.d.ts.map +1 -1
  14. package/dist/protocol/types.d.ts +1 -1
  15. package/dist/protocol/types.d.ts.map +1 -1
  16. package/dist/recorder/closure.d.ts.map +1 -1
  17. package/dist/recorder/core.d.ts +10 -1
  18. package/dist/recorder/core.d.ts.map +1 -1
  19. package/dist/recorder/device.d.ts.map +1 -1
  20. package/dist/recorder/encoder.d.ts.map +1 -1
  21. package/dist/recorder/pass.d.ts.map +1 -1
  22. package/dist/recorder/pipeline.d.ts +5 -0
  23. package/dist/recorder/pipeline.d.ts.map +1 -0
  24. package/dist/recorder/proxy.d.ts +3 -1
  25. package/dist/recorder/proxy.d.ts.map +1 -1
  26. package/dist/recorder/session.d.ts +1 -1
  27. package/dist/recorder/session.d.ts.map +1 -1
  28. package/dist/recorder/shader.d.ts +3 -1
  29. package/dist/recorder/shader.d.ts.map +1 -1
  30. package/dist/recorder.d.ts +2 -2
  31. package/dist/recorder.d.ts.map +1 -1
  32. package/dist/replay/execute.d.ts.map +1 -1
  33. package/dist/replay/resources.d.ts +4 -1
  34. package/dist/replay/resources.d.ts.map +1 -1
  35. package/dist/replay/session.d.ts +7 -0
  36. package/dist/replay/session.d.ts.map +1 -1
  37. package/dist/types.d.ts +39 -4
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +6 -6
  40. package/src/__tests__/coverage-invariant.test-d.ts +4 -5
  41. package/src/__tests__/coverage-invariant.unit.test.ts +6 -17
  42. package/src/__tests__/e2e.browser.test.ts +50 -0
  43. package/src/__tests__/guard-gates.test.ts +1 -1
  44. package/src/__tests__/query-set-lifecycle.unit.test.ts +265 -0
  45. package/src/__tests__/query-set-replay-fixture.ts +553 -0
  46. package/src/__tests__/readback-format-matrix.dawn.test.ts +7 -0
  47. package/src/__tests__/recorder-canvas-context.unit.test.ts +67 -0
  48. package/src/__tests__/recorder-session.integration.test.ts +55 -0
  49. package/src/__tests__/recorder-session.unit.test.ts +18 -1
  50. package/src/__tests__/replay-session.dawn.test.ts +49 -0
  51. package/src/__tests__/replay-session.test-d.ts +1 -0
  52. package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +61 -0
  53. package/src/index.ts +1 -0
  54. package/src/protocol/event-semantics.ts +20 -2
  55. package/src/protocol/types.ts +1 -0
  56. package/src/protocol/validation.ts +31 -0
  57. package/src/recorder/assemble.ts +8 -1
  58. package/src/recorder/closure.ts +17 -0
  59. package/src/recorder/core.ts +11 -0
  60. package/src/recorder/device.ts +33 -6
  61. package/src/recorder/encoder.ts +22 -4
  62. package/src/recorder/pass.ts +7 -2
  63. package/src/recorder/pipeline.ts +31 -0
  64. package/src/recorder/proxy.ts +58 -2
  65. package/src/recorder/session.ts +1 -1
  66. package/src/recorder/shader.ts +29 -22
  67. package/src/recorder.ts +6 -2
  68. package/src/replay/execute.ts +111 -0
  69. package/src/replay/readback.ts +8 -3
  70. package/src/replay/resources.ts +6 -0
  71. package/src/replay/session.ts +234 -25
  72. package/src/types.ts +53 -12
@@ -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
- const result = await executeEvent(context, event, -bootstrapIndex - 1);
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
- for (const slice of resource.initialData) {
355
- const blob = context.tape.blobs.find((candidate) => candidate.hash === slice.hash);
356
- if (blob === undefined) {
357
- return eventFailure(-bootstrapIndex - 1, event, 'lookup', `blob ${slice.hash} is missing`);
358
- }
359
- const end = slice.byteOffset + slice.byteLength;
360
- if (slice.byteOffset < 0 || slice.byteLength < 0 || end > blob.bytes.byteLength) {
361
- return eventFailure(
362
- -bootstrapIndex - 1,
363
- event,
364
- 'lookup',
365
- `blob ${slice.hash} does not contain initialData slice [${slice.byteOffset}, ${end})`,
366
- );
367
- }
368
- const bytes = blob.bytes.slice(slice.byteOffset, slice.byteOffset + slice.byteLength);
369
- const written = context.queue.writeBuffer(entry.resource.value, 0, bytes);
370
- if (!written.ok) {
371
- return eventFailure(-bootstrapIndex - 1, event, 'write', written.error);
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
- try {
493
- (entry.resource.value as RhiRenderPassEncoder | RhiComputePassEncoder).end();
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
- // The ForgeaX encodeEmptyComputePass compound operation is represented by
6
- // its existing beginComputePass/endComputePass event pair.
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
- readonly occlusionQuerySet?: unknown | undefined;
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
- * Excludes: resolveQuerySet (OOS-3),
559
- * executeBundles (OOS-10), beginOcclusionQuery/endOcclusionQuery.
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']);