@forgeax/engine-rhi-debug 0.1.28 → 0.1.30

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 (75) hide show
  1. package/README.md +32 -7
  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/__tests__/timestamp-query-capture.dawn.test.d.ts +2 -0
  9. package/dist/__tests__/timestamp-query-capture.dawn.test.d.ts.map +1 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.mjs +527 -58
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/protocol/event-semantics.d.ts +1 -1
  15. package/dist/protocol/event-semantics.d.ts.map +1 -1
  16. package/dist/protocol/types.d.ts +1 -1
  17. package/dist/protocol/types.d.ts.map +1 -1
  18. package/dist/recorder/closure.d.ts.map +1 -1
  19. package/dist/recorder/core.d.ts +10 -1
  20. package/dist/recorder/core.d.ts.map +1 -1
  21. package/dist/recorder/device.d.ts.map +1 -1
  22. package/dist/recorder/encoder.d.ts.map +1 -1
  23. package/dist/recorder/pass.d.ts.map +1 -1
  24. package/dist/recorder/pipeline.d.ts +5 -0
  25. package/dist/recorder/pipeline.d.ts.map +1 -0
  26. package/dist/recorder/proxy.d.ts +3 -1
  27. package/dist/recorder/proxy.d.ts.map +1 -1
  28. package/dist/recorder/session.d.ts +1 -1
  29. package/dist/recorder/session.d.ts.map +1 -1
  30. package/dist/recorder/shader.d.ts +3 -1
  31. package/dist/recorder/shader.d.ts.map +1 -1
  32. package/dist/recorder.d.ts +2 -2
  33. package/dist/recorder.d.ts.map +1 -1
  34. package/dist/replay/execute.d.ts.map +1 -1
  35. package/dist/replay/resources.d.ts +4 -1
  36. package/dist/replay/resources.d.ts.map +1 -1
  37. package/dist/replay/session.d.ts +7 -0
  38. package/dist/replay/session.d.ts.map +1 -1
  39. package/dist/types.d.ts +45 -6
  40. package/dist/types.d.ts.map +1 -1
  41. package/package.json +6 -6
  42. package/src/__tests__/coverage-invariant.test-d.ts +4 -5
  43. package/src/__tests__/coverage-invariant.unit.test.ts +6 -17
  44. package/src/__tests__/e2e.browser.test.ts +50 -0
  45. package/src/__tests__/guard-gates.test.ts +1 -1
  46. package/src/__tests__/query-set-lifecycle.unit.test.ts +265 -0
  47. package/src/__tests__/query-set-replay-fixture.ts +553 -0
  48. package/src/__tests__/readback-format-matrix.dawn.test.ts +9 -2
  49. package/src/__tests__/recorder-canvas-context.unit.test.ts +67 -0
  50. package/src/__tests__/recorder-session.integration.test.ts +55 -0
  51. package/src/__tests__/recorder-session.unit.test.ts +18 -1
  52. package/src/__tests__/replay-session.dawn.test.ts +171 -0
  53. package/src/__tests__/replay-session.test-d.ts +1 -0
  54. package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +61 -0
  55. package/src/__tests__/timestamp-query-capture.dawn.test.ts +61 -0
  56. package/src/index.ts +1 -0
  57. package/src/protocol/event-semantics.ts +21 -2
  58. package/src/protocol/types.ts +1 -0
  59. package/src/protocol/validation.ts +36 -0
  60. package/src/recorder/assemble.ts +8 -1
  61. package/src/recorder/closure.ts +21 -1
  62. package/src/recorder/core.ts +11 -0
  63. package/src/recorder/device.ts +33 -6
  64. package/src/recorder/encoder.ts +60 -6
  65. package/src/recorder/pass.ts +7 -2
  66. package/src/recorder/pipeline.ts +31 -0
  67. package/src/recorder/proxy.ts +58 -2
  68. package/src/recorder/session.ts +1 -1
  69. package/src/recorder/shader.ts +29 -22
  70. package/src/recorder.ts +6 -2
  71. package/src/replay/execute.ts +143 -1
  72. package/src/replay/readback.ts +15 -3
  73. package/src/replay/resources.ts +6 -0
  74. package/src/replay/session.ts +241 -27
  75. package/src/types.ts +61 -14
package/dist/index.mjs CHANGED
@@ -4294,11 +4294,14 @@ var eventKinds = [
4294
4294
  "frameMark",
4295
4295
  "createBuffer",
4296
4296
  "createTexture",
4297
+ "createQuerySet",
4297
4298
  "destroyBuffer",
4298
4299
  "destroyTexture",
4300
+ "destroyQuerySet",
4299
4301
  "createTextureView",
4300
4302
  "createSampler",
4301
4303
  "createBindGroupLayout",
4304
+ "getBindGroupLayout",
4302
4305
  "createBindGroup",
4303
4306
  "createPipelineLayout",
4304
4307
  "createRenderPipeline",
@@ -4310,12 +4313,15 @@ var eventKinds = [
4310
4313
  "copyExternalImageToTexture",
4311
4314
  "submit",
4312
4315
  "beginRenderPass",
4316
+ "beginOcclusionQuery",
4317
+ "endOcclusionQuery",
4313
4318
  "beginComputePass",
4314
4319
  "copyBufferToBuffer",
4315
4320
  "copyBufferToTexture",
4316
4321
  "copyTextureToBuffer",
4317
4322
  "copyTextureToTexture",
4318
4323
  "clearBuffer",
4324
+ "resolveQuerySet",
4319
4325
  "pushDebugGroup",
4320
4326
  "popDebugGroup",
4321
4327
  "insertDebugMarker",
@@ -4362,6 +4368,8 @@ function resourceKindForEvent(kind) {
4362
4368
  return "buffer";
4363
4369
  case "createTexture":
4364
4370
  return "texture";
4371
+ case "createQuerySet":
4372
+ return "query-set";
4365
4373
  case "createTextureView":
4366
4374
  return "texture-view";
4367
4375
  case "createSampler":
@@ -4373,6 +4381,7 @@ function resourceKindForEvent(kind) {
4373
4381
  return "pipeline";
4374
4382
  case "createBindGroup":
4375
4383
  case "createBindGroupLayout":
4384
+ case "getBindGroupLayout":
4376
4385
  case "createPipelineLayout":
4377
4386
  return "binding";
4378
4387
  case "createCommandEncoder":
@@ -4388,12 +4397,12 @@ function semanticsFor(kind) {
4388
4397
  created: (event) => createdHandles(event),
4389
4398
  read: (event) => referencedHandles(event),
4390
4399
  written: (event) => writtenHandles(event),
4391
- destroyed: (event) => event.kind === "destroyBuffer" || event.kind === "destroyTexture" ? stringField(event, "handleId") : []
4400
+ destroyed: (event) => event.kind === "destroyBuffer" || event.kind === "destroyTexture" || event.kind === "destroyQuerySet" ? stringField(event, "handleId") : []
4392
4401
  };
4393
4402
  }
4394
4403
  function categoryFor(kind) {
4395
4404
  if (isWorkEvent(kind)) return "work";
4396
- if (kind.startsWith("create") || kind.startsWith("destroy") || kind === "initialData")
4405
+ if (kind.startsWith("create") || kind.startsWith("destroy") || kind === "initialData" || kind === "getBindGroupLayout")
4397
4406
  return "resource";
4398
4407
  if (kind.includes("Pass")) return "pass";
4399
4408
  if (kind.startsWith("copy") || kind === "clearBuffer" || kind.startsWith("write")) return "copy";
@@ -4422,6 +4431,9 @@ function referencedHandles(event) {
4422
4431
  "resourceHandleIds",
4423
4432
  "bindGroupHandleId",
4424
4433
  "pipelineHandleId",
4434
+ "querySetHandleId",
4435
+ "occlusionQuerySetHandleId",
4436
+ "timestampQuerySetHandleId",
4425
4437
  "indexBufferHandleId",
4426
4438
  "vertexBufferHandleId"
4427
4439
  ];
@@ -5256,6 +5268,7 @@ function isResourceKind(value) {
5256
5268
  return [
5257
5269
  "buffer",
5258
5270
  "texture",
5271
+ "query-set",
5259
5272
  "texture-view",
5260
5273
  "sampler",
5261
5274
  "shader-module",
@@ -5268,8 +5281,10 @@ function eventResources(event) {
5268
5281
  switch (event.kind) {
5269
5282
  case "createBuffer":
5270
5283
  case "createTexture":
5284
+ case "createQuerySet":
5271
5285
  case "createSampler":
5272
5286
  case "createBindGroupLayout":
5287
+ case "getBindGroupLayout":
5273
5288
  case "createBindGroup":
5274
5289
  case "createPipelineLayout":
5275
5290
  case "createRenderPipeline":
@@ -5279,6 +5294,7 @@ function eventResources(event) {
5279
5294
  created: [event.handleId],
5280
5295
  reads: handleRefs(event, [
5281
5296
  "layoutHandleId",
5297
+ "pipelineHandleId",
5282
5298
  "vertexShaderModuleHandleId",
5283
5299
  "fragmentShaderModuleHandleId",
5284
5300
  "computeShaderModuleHandleId"
@@ -5291,6 +5307,7 @@ function eventResources(event) {
5291
5307
  return { created: [event.cmdHandleId], reads: [], destroyed: [] };
5292
5308
  case "destroyBuffer":
5293
5309
  case "destroyTexture":
5310
+ case "destroyQuerySet":
5294
5311
  return { created: [], reads: [event.handleId], destroyed: [event.handleId] };
5295
5312
  case "writeBuffer":
5296
5313
  case "writeTexture":
@@ -5312,6 +5329,37 @@ function eventResources(event) {
5312
5329
  ]),
5313
5330
  destroyed: []
5314
5331
  };
5332
+ case "resolveQuerySet":
5333
+ return {
5334
+ created: [],
5335
+ reads: handleRefs(event, ["cmdHandleId", "querySetHandleId", "destinationHandleId"]),
5336
+ destroyed: []
5337
+ };
5338
+ case "beginRenderPass":
5339
+ return {
5340
+ created: [event.passHandleId],
5341
+ reads: handleRefs(event, [
5342
+ "cmdHandleId",
5343
+ "occlusionQuerySetHandleId",
5344
+ "timestampQuerySetHandleId",
5345
+ "colorAttachmentViewHandleIds",
5346
+ "colorAttachmentResolveTargetHandleIds",
5347
+ "depthStencilViewHandleId"
5348
+ ]),
5349
+ destroyed: []
5350
+ };
5351
+ case "beginComputePass":
5352
+ return {
5353
+ created: [event.passHandleId],
5354
+ reads: handleRefs(event, ["cmdHandleId", "timestampQuerySetHandleId"]),
5355
+ destroyed: []
5356
+ };
5357
+ case "endRenderPass":
5358
+ case "endComputePass":
5359
+ return { created: [], reads: [event.passHandleId], destroyed: [event.passHandleId] };
5360
+ case "beginOcclusionQuery":
5361
+ case "endOcclusionQuery":
5362
+ return { created: [], reads: handleRefs(event, ["passHandleId"]), destroyed: [] };
5315
5363
  default:
5316
5364
  return { created: [], reads: [], destroyed: [] };
5317
5365
  }
@@ -6098,7 +6146,8 @@ function createdHandleId(event) {
6098
6146
  if (event.kind === "beginRenderPass" || event.kind === "beginComputePass") {
6099
6147
  return event.passHandleId;
6100
6148
  }
6101
- if (event.kind.startsWith("create") && "handleId" in event) return event.handleId;
6149
+ if ((event.kind.startsWith("create") || event.kind === "getBindGroupLayout") && "handleId" in event)
6150
+ return event.handleId;
6102
6151
  return void 0;
6103
6152
  }
6104
6153
  function resourceKind(event) {
@@ -6107,6 +6156,8 @@ function resourceKind(event) {
6107
6156
  return "buffer";
6108
6157
  case "createTexture":
6109
6158
  return "texture";
6159
+ case "createQuerySet":
6160
+ return "query-set";
6110
6161
  case "createTextureView":
6111
6162
  return "texture-view";
6112
6163
  case "createSampler":
@@ -6118,6 +6169,7 @@ function resourceKind(event) {
6118
6169
  return "pipeline";
6119
6170
  case "createBindGroup":
6120
6171
  case "createBindGroupLayout":
6172
+ case "getBindGroupLayout":
6121
6173
  case "createPipelineLayout":
6122
6174
  return "binding";
6123
6175
  case "createCommandEncoder":
@@ -6263,11 +6315,14 @@ function _collectFrameReferencedHandleIds(events) {
6263
6315
  if (vhId !== void 0) refs.add(vhId);
6264
6316
  }
6265
6317
  if (we.depthStencilViewHandleId !== void 0) refs.add(we.depthStencilViewHandleId);
6318
+ if (we.occlusionQuerySetHandleId !== void 0) refs.add(we.occlusionQuerySetHandleId);
6319
+ if (we.timestampQuerySetHandleId !== void 0) refs.add(we.timestampQuerySetHandleId);
6266
6320
  break;
6267
6321
  }
6268
6322
  case "beginComputePass": {
6269
6323
  const we = e;
6270
6324
  refs.add(we.cmdHandleId);
6325
+ if (we.timestampQuerySetHandleId !== void 0) refs.add(we.timestampQuerySetHandleId);
6271
6326
  break;
6272
6327
  }
6273
6328
  case "finish": {
@@ -6317,6 +6372,7 @@ function _collectFrameReferencedHandleIds(events) {
6317
6372
  break;
6318
6373
  }
6319
6374
  case "createBindGroup":
6375
+ case "getBindGroupLayout":
6320
6376
  case "createPipelineLayout":
6321
6377
  case "createRenderPipeline":
6322
6378
  case "createComputePipeline":
@@ -6326,8 +6382,27 @@ function _collectFrameReferencedHandleIds(events) {
6326
6382
  }
6327
6383
  case "destroyBuffer":
6328
6384
  case "destroyTexture":
6385
+ case "destroyQuerySet":
6329
6386
  refs.add(e.handleId);
6330
6387
  break;
6388
+ case "frameMark":
6389
+ case "createBuffer":
6390
+ case "createTexture":
6391
+ case "createQuerySet":
6392
+ case "createSampler":
6393
+ case "createBindGroupLayout":
6394
+ case "createShaderModule":
6395
+ case "createCommandEncoder":
6396
+ break;
6397
+ case "resolveQuerySet":
6398
+ refs.add(e.cmdHandleId);
6399
+ refs.add(e.querySetHandleId);
6400
+ refs.add(e.destinationHandleId);
6401
+ break;
6402
+ case "beginOcclusionQuery":
6403
+ case "endOcclusionQuery":
6404
+ refs.add(e.passHandleId);
6405
+ break;
6331
6406
  }
6332
6407
  }
6333
6408
  return refs;
@@ -6361,8 +6436,11 @@ function _getCreateEventReferencedHandleIds(event) {
6361
6436
  const e = event;
6362
6437
  return [e.sourceHandleId];
6363
6438
  }
6439
+ case "getBindGroupLayout":
6440
+ return [event.pipelineHandleId];
6364
6441
  case "createBuffer":
6365
6442
  case "createTexture":
6443
+ case "createQuerySet":
6366
6444
  case "createSampler":
6367
6445
  case "createBindGroupLayout":
6368
6446
  case "createShaderModule":
@@ -6608,18 +6686,21 @@ function getHandleId(s, handle, kind) {
6608
6686
  }
6609
6687
 
6610
6688
  // src/recorder/shader.ts
6689
+ function recordShaderModule(debugInst, result, desc) {
6690
+ if (!result.ok) return result;
6691
+ const hId = debugInst.pushExternalCreateEvent(result.value, "shaderModule", {
6692
+ kind: "createShaderModule",
6693
+ handleId: "",
6694
+ wgslCode: desc.code
6695
+ });
6696
+ debugInst.registerShaderModule(result.value, hId);
6697
+ return result;
6698
+ }
6611
6699
  function wrapCreateShaderModule(originalFn, debugInst) {
6612
6700
  return async function wrappedCreateShaderModule(device, desc) {
6613
6701
  const realDevice = device._realDevice ?? device;
6614
6702
  const result = await originalFn(realDevice, desc);
6615
- if (!result.ok) return result;
6616
- const hId = debugInst.pushExternalCreateEvent(result.value, "shaderModule", {
6617
- kind: "createShaderModule",
6618
- handleId: "",
6619
- wgslCode: desc.code
6620
- });
6621
- debugInst.registerShaderModule(result.value, hId);
6622
- return result;
6703
+ return recordShaderModule(debugInst, result, desc);
6623
6704
  };
6624
6705
  }
6625
6706
 
@@ -6782,10 +6863,15 @@ function createRenderPassProxy(s, realPass, passHId) {
6782
6863
  return realPass.executeBundles(bundles);
6783
6864
  },
6784
6865
  beginOcclusionQuery(queryIndex) {
6785
- return realPass.beginOcclusionQuery(queryIndex);
6866
+ const result = realPass.beginOcclusionQuery(queryIndex);
6867
+ if (result.ok)
6868
+ pushEvent(s, { kind: "beginOcclusionQuery", passHandleId: passHId, queryIndex });
6869
+ return result;
6786
6870
  },
6787
6871
  endOcclusionQuery() {
6788
- return realPass.endOcclusionQuery();
6872
+ const result = realPass.endOcclusionQuery();
6873
+ if (result.ok) pushEvent(s, { kind: "endOcclusionQuery", passHandleId: passHId });
6874
+ return result;
6789
6875
  },
6790
6876
  end() {
6791
6877
  pushEvent(s, { kind: "endRenderPass", passHandleId: passHId });
@@ -6870,18 +6956,25 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
6870
6956
  kind: "beginRenderPass",
6871
6957
  cmdHandleId: cmdHId,
6872
6958
  passHandleId: passHId,
6959
+ timestampQuerySetHandleId: desc?.timestampWrites === void 0 ? void 0 : getHandleId(s, desc.timestampWrites.querySet, "querySet"),
6873
6960
  desc: {
6874
6961
  colorAttachments: Array.from(desc.colorAttachments).map(
6875
6962
  (attachment) => attachment === null || attachment === void 0 ? attachment : { ...attachment }
6876
6963
  ),
6877
6964
  ...desc.depthStencilAttachment === void 0 ? {} : { depthStencilAttachment: { ...desc.depthStencilAttachment } },
6878
- ...desc.occlusionQuerySet === void 0 ? {} : { occlusionQuerySet: desc.occlusionQuerySet },
6879
- ...desc.timestampWrites === void 0 ? {} : { timestampWrites: desc.timestampWrites },
6965
+ ...desc.timestampWrites === void 0 ? {} : { timestampWrites: recordTimestampWrites(desc.timestampWrites) },
6880
6966
  ...desc.maxDrawCount === void 0 ? {} : { maxDrawCount: desc.maxDrawCount }
6881
6967
  },
6882
6968
  colorAttachmentViewHandleIds,
6883
6969
  colorAttachmentResolveTargetHandleIds,
6884
- depthStencilViewHandleId
6970
+ depthStencilViewHandleId,
6971
+ ...desc.occlusionQuerySet === void 0 ? {} : {
6972
+ occlusionQuerySetHandleId: getHandleId(
6973
+ s,
6974
+ desc.occlusionQuerySet,
6975
+ "querySet"
6976
+ )
6977
+ }
6885
6978
  });
6886
6979
  const realPass = realEnc.beginRenderPass(desc);
6887
6980
  return createRenderPassProxy(s, realPass, passHId);
@@ -6892,7 +6985,11 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
6892
6985
  kind: "beginComputePass",
6893
6986
  cmdHandleId: cmdHId,
6894
6987
  passHandleId: passHId,
6895
- desc
6988
+ timestampQuerySetHandleId: desc?.timestampWrites === void 0 ? void 0 : getHandleId(s, desc.timestampWrites.querySet, "querySet"),
6989
+ desc: desc === void 0 ? void 0 : {
6990
+ ...desc.label === void 0 ? {} : { label: desc.label },
6991
+ timestampWrites: recordTimestampWrites(desc.timestampWrites)
6992
+ }
6896
6993
  });
6897
6994
  const realPass = realEnc.beginComputePass(desc);
6898
6995
  return createComputePassProxy(s, realPass, passHId);
@@ -6903,7 +7000,11 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
6903
7000
  kind: "beginComputePass",
6904
7001
  cmdHandleId: cmdHId,
6905
7002
  passHandleId: passHId,
6906
- desc
7003
+ timestampQuerySetHandleId: desc?.timestampWrites === void 0 ? void 0 : getHandleId(s, desc.timestampWrites.querySet, "querySet"),
7004
+ desc: {
7005
+ ...desc.label === void 0 ? {} : { label: desc.label },
7006
+ timestampWrites: recordTimestampWrites(desc.timestampWrites)
7007
+ }
6907
7008
  });
6908
7009
  realEnc.encodeEmptyComputePass(desc);
6909
7010
  pushEvent(s, { kind: "endComputePass", passHandleId: passHId });
@@ -7021,13 +7122,25 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
7021
7122
  realEnc.clearBuffer(buffer, offset, size);
7022
7123
  },
7023
7124
  resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
7024
- return realEnc.resolveQuerySet(
7125
+ const result = realEnc.resolveQuerySet(
7025
7126
  querySet,
7026
7127
  firstQuery,
7027
7128
  queryCount,
7028
7129
  destination,
7029
7130
  destinationOffset
7030
7131
  );
7132
+ if (result.ok) {
7133
+ pushEvent(s, {
7134
+ kind: "resolveQuerySet",
7135
+ cmdHandleId: cmdHId,
7136
+ querySetHandleId: getHandleId(s, querySet, "querySet"),
7137
+ firstQuery,
7138
+ queryCount,
7139
+ destinationHandleId: getHandleId(s, destination, "buffer"),
7140
+ destinationOffset
7141
+ });
7142
+ }
7143
+ return result;
7031
7144
  },
7032
7145
  pushDebugGroup(groupLabel) {
7033
7146
  pushEvent(s, { kind: "pushDebugGroup", cmdHandleId: cmdHId, groupLabel });
@@ -7051,6 +7164,35 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
7051
7164
  }
7052
7165
  };
7053
7166
  }
7167
+ function recordTimestampWrites(writes) {
7168
+ return writes === void 0 ? void 0 : {
7169
+ ...writes.beginningOfPassWriteIndex === void 0 ? {} : { beginningOfPassWriteIndex: writes.beginningOfPassWriteIndex },
7170
+ ...writes.endOfPassWriteIndex === void 0 ? {} : { endOfPassWriteIndex: writes.endOfPassWriteIndex }
7171
+ };
7172
+ }
7173
+
7174
+ // src/recorder/pipeline.ts
7175
+ function wrapPipeline(state, pipeline, pipelineHandleId) {
7176
+ const operation = pipeline.getBindGroupLayout;
7177
+ if (operation === void 0) return pipeline;
7178
+ const getLayout = operation.bind(pipeline);
7179
+ Object.defineProperty(pipeline, "getBindGroupLayout", {
7180
+ configurable: true,
7181
+ value: (index) => {
7182
+ const layout = getLayout(index);
7183
+ const event = {
7184
+ kind: "getBindGroupLayout",
7185
+ handleId: "",
7186
+ pipelineHandleId,
7187
+ index
7188
+ };
7189
+ registerHandle(state, layout, "bindGroupLayout", event);
7190
+ pushEvent(state, event);
7191
+ return layout;
7192
+ }
7193
+ });
7194
+ return pipeline;
7195
+ }
7054
7196
 
7055
7197
  // src/recorder/queue.ts
7056
7198
  function createQueueProxy(s, realQueue) {
@@ -7381,9 +7523,9 @@ function createDeviceProxy(s, realDevice) {
7381
7523
  vertexShaderModuleHandleId,
7382
7524
  fragmentShaderModuleHandleId
7383
7525
  };
7384
- registerHandle(s, res.value, "renderPipeline", event);
7526
+ const pipelineId = registerHandle(s, res.value, "renderPipeline", event);
7385
7527
  pushEvent(s, event);
7386
- return res;
7528
+ return ok(wrapPipeline(s, res.value, pipelineId));
7387
7529
  },
7388
7530
  createComputePipeline(desc) {
7389
7531
  const res = realDevice.createComputePipeline(desc);
@@ -7406,12 +7548,25 @@ function createDeviceProxy(s, realDevice) {
7406
7548
  layoutHandleId: layoutId,
7407
7549
  computeShaderModuleHandleId
7408
7550
  };
7409
- registerHandle(s, res.value, "computePipeline", event);
7551
+ const pipelineId = registerHandle(s, res.value, "computePipeline", event);
7410
7552
  pushEvent(s, event);
7411
- return res;
7553
+ return ok(wrapPipeline(s, res.value, pipelineId));
7412
7554
  },
7413
7555
  createQuerySet(desc) {
7414
- return realDevice.createQuerySet(desc);
7556
+ const res = realDevice.createQuerySet(desc);
7557
+ if (!res.ok) return res;
7558
+ const event = {
7559
+ kind: "createQuerySet",
7560
+ handleId: "",
7561
+ desc: {
7562
+ ...desc.label === void 0 ? {} : { label: desc.label },
7563
+ type: desc.type ?? "occlusion",
7564
+ count: desc.count ?? 0
7565
+ }
7566
+ };
7567
+ registerHandle(s, res.value, "querySet", event);
7568
+ pushEvent(s, event);
7569
+ return res;
7415
7570
  },
7416
7571
  destroyBuffer(buf) {
7417
7572
  const hId = s.handleMap.get(buf);
@@ -7427,7 +7582,16 @@ function createDeviceProxy(s, realDevice) {
7427
7582
  return res;
7428
7583
  },
7429
7584
  destroyQuerySet(querySet) {
7430
- return realDevice.destroyQuerySet(querySet);
7585
+ const hId = s.handleMap.get(querySet);
7586
+ const res = realDevice.destroyQuerySet(querySet);
7587
+ if (!res.ok) return res;
7588
+ if (hId !== void 0) {
7589
+ if (!retainsCaptureBootstrap(s) && !s.snapshotSeededHandles.has(hId) && !hasBootstrapDependency(s, hId)) {
7590
+ s.bootstrapCreates.delete(hId);
7591
+ }
7592
+ pushEvent(s, { kind: "destroyQuerySet", handleId: hId });
7593
+ }
7594
+ return res;
7431
7595
  },
7432
7596
  destroyTexture(tex) {
7433
7597
  const hId = s.handleMap.get(tex);
@@ -8180,15 +8344,46 @@ function wrap(instance) {
8180
8344
  function createRecorderProxy(backend) {
8181
8345
  const recorder = wrap(backend.rhi);
8182
8346
  const wrappedCreateShaderModule = wrapCreateShaderModule(backend.createShaderModule, recorder);
8347
+ const originalCreateShaderModuleImmediate = backend.createShaderModuleImmediate;
8348
+ const wrappedCreateShaderModuleImmediate = originalCreateShaderModuleImmediate === void 0 ? void 0 : (device, desc) => {
8349
+ const realDevice = device._realDevice ?? device;
8350
+ const result = originalCreateShaderModuleImmediate(realDevice, desc);
8351
+ if (!result.ok) return result;
8352
+ const hId = recorder.pushExternalCreateEvent(result.value, "shaderModule", {
8353
+ kind: "createShaderModule",
8354
+ handleId: "",
8355
+ wgslCode: desc.code
8356
+ });
8357
+ recorder.registerShaderModule(result.value, hId);
8358
+ return result;
8359
+ };
8183
8360
  const acquireCanvasContext = backend.rhi.acquireCanvasContext;
8184
8361
  const rhi = {
8185
8362
  requestAdapter: recorder.requestAdapter.bind(recorder),
8186
8363
  createShaderModule: wrappedCreateShaderModule,
8187
- ...acquireCanvasContext === void 0 ? {} : { acquireCanvasContext: acquireCanvasContext.bind(backend.rhi) }
8364
+ ...wrappedCreateShaderModuleImmediate === void 0 ? {} : { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate },
8365
+ ...acquireCanvasContext === void 0 ? {} : {
8366
+ acquireCanvasContext(canvas) {
8367
+ const result = acquireCanvasContext.call(backend.rhi, canvas);
8368
+ if (!result.ok) return result;
8369
+ const context = result.value;
8370
+ return ok({
8371
+ ...context,
8372
+ configure(configuration) {
8373
+ const device = configuration.device._realDevice;
8374
+ return context.configure({
8375
+ ...configuration,
8376
+ ...device === void 0 ? {} : { device }
8377
+ });
8378
+ }
8379
+ });
8380
+ }
8381
+ }
8188
8382
  };
8189
8383
  const wrappedBackend = {
8190
8384
  rhi,
8191
8385
  createShaderModule: wrappedCreateShaderModule,
8386
+ ...wrappedCreateShaderModuleImmediate === void 0 ? {} : { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate },
8192
8387
  unwrapDeviceForSurface(device) {
8193
8388
  const raw = device._realDevice;
8194
8389
  if (raw !== void 0) return ok(raw);
@@ -8619,10 +8814,20 @@ async function executeEvent(context, event, eventIndex) {
8619
8814
  kind: "texture"
8620
8815
  }
8621
8816
  );
8817
+ case "createQuerySet":
8818
+ return createResource(
8819
+ context,
8820
+ eventIndex,
8821
+ event,
8822
+ context.device.createQuerySet(event.desc),
8823
+ { kind: "query-set" }
8824
+ );
8622
8825
  case "destroyBuffer":
8623
8826
  return destroyResource(context, eventIndex, event, "buffer");
8624
8827
  case "destroyTexture":
8625
8828
  return destroyResource(context, eventIndex, event, "texture");
8829
+ case "destroyQuerySet":
8830
+ return destroyQuerySet(context, event, eventIndex);
8626
8831
  case "createTextureView": {
8627
8832
  const texture = requireResource(
8628
8833
  context,
@@ -8659,6 +8864,25 @@ async function executeEvent(context, event, eventIndex) {
8659
8864
  context.device.createBindGroupLayout(event.desc),
8660
8865
  { kind: "binding", role: "bind-group-layout" }
8661
8866
  );
8867
+ case "getBindGroupLayout": {
8868
+ const pipeline = requireReplayResource(
8869
+ context,
8870
+ event.pipelineHandleId,
8871
+ "pipeline",
8872
+ eventIndex,
8873
+ event.kind
8874
+ );
8875
+ if (!pipeline.ok) return pipeline;
8876
+ return createResource(
8877
+ context,
8878
+ eventIndex,
8879
+ event,
8880
+ ok(
8881
+ pipeline.value.value.getBindGroupLayout(event.index)
8882
+ ),
8883
+ { kind: "binding", role: "bind-group-layout" }
8884
+ );
8885
+ }
8662
8886
  case "createBindGroup":
8663
8887
  return createBindGroup(context, event, eventIndex);
8664
8888
  case "createPipelineLayout":
@@ -8690,6 +8914,18 @@ async function executeEvent(context, event, eventIndex) {
8690
8914
  return beginRenderPass(context, event, eventIndex);
8691
8915
  case "beginComputePass":
8692
8916
  return beginComputePass(context, event, eventIndex);
8917
+ case "resolveQuerySet":
8918
+ return resolveQuerySet(context, event, eventIndex);
8919
+ case "beginOcclusionQuery":
8920
+ return passCall(context, event, eventIndex, "render-pass", (pass) => {
8921
+ const result = pass.beginOcclusionQuery(event.queryIndex);
8922
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
8923
+ });
8924
+ case "endOcclusionQuery":
8925
+ return passCall(context, event, eventIndex, "render-pass", (pass) => {
8926
+ const result = pass.endOcclusionQuery();
8927
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
8928
+ });
8693
8929
  case "copyBufferToBuffer":
8694
8930
  return copyBufferToBuffer(context, event, eventIndex);
8695
8931
  case "copyBufferToTexture":
@@ -8920,6 +9156,50 @@ function destroyResource(context, eventIndex, event, kind) {
8920
9156
  context.table.delete(event.handleId);
8921
9157
  return ok(void 0);
8922
9158
  }
9159
+ function destroyQuerySet(context, event, eventIndex) {
9160
+ const entry = context.table.get(event.handleId);
9161
+ if (entry?.resource.kind !== "query-set")
9162
+ return missingResource(eventIndex, event, event.handleId, "query-set");
9163
+ const result = context.device.destroyQuerySet(entry.resource.value);
9164
+ if (!result.ok) return eventFailure(eventIndex, event, "create", result.error);
9165
+ context.table.delete(event.handleId);
9166
+ return ok(void 0);
9167
+ }
9168
+ function resolveQuerySet(context, event, eventIndex) {
9169
+ const encoder = requireResource(
9170
+ context,
9171
+ event.cmdHandleId,
9172
+ "encoder",
9173
+ eventIndex,
9174
+ event.kind,
9175
+ "command"
9176
+ );
9177
+ const querySet = requireResource(
9178
+ context,
9179
+ event.querySetHandleId,
9180
+ "query-set",
9181
+ eventIndex,
9182
+ event.kind
9183
+ );
9184
+ const destination = requireResource(
9185
+ context,
9186
+ event.destinationHandleId,
9187
+ "buffer",
9188
+ eventIndex,
9189
+ event.kind
9190
+ );
9191
+ if (!encoder.ok) return encoder;
9192
+ if (!querySet.ok) return querySet;
9193
+ if (!destination.ok) return destination;
9194
+ const result = encoder.value.resolveQuerySet(
9195
+ querySet.value,
9196
+ event.firstQuery,
9197
+ event.queryCount,
9198
+ destination.value,
9199
+ event.destinationOffset
9200
+ );
9201
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
9202
+ }
8923
9203
  async function createShaderModule(context, event, eventIndex) {
8924
9204
  const result = await context.createShaderModule(context.device, { code: event.wgslCode });
8925
9205
  return createResource(context, eventIndex, event, result, { kind: "shader-module" });
@@ -9145,15 +9425,23 @@ function beginRenderPass(context, event, eventIndex) {
9145
9425
  };
9146
9426
  });
9147
9427
  const depth = event.depthStencilViewHandleId === void 0 ? void 0 : context.table.get(event.depthStencilViewHandleId)?.resource;
9428
+ const querySet = event.occlusionQuerySetHandleId === void 0 ? void 0 : context.table.get(event.occlusionQuerySetHandleId)?.resource;
9429
+ if (event.occlusionQuerySetHandleId !== void 0 && querySet?.kind !== "query-set") {
9430
+ return missingResource(eventIndex, event, event.occlusionQuerySetHandleId, "query-set");
9431
+ }
9432
+ const timestamp = replayTimestampWrites(context, event, eventIndex);
9433
+ if (!timestamp.ok) return timestamp;
9148
9434
  const descriptor = {
9149
9435
  ...event.desc,
9436
+ ...timestamp.value === void 0 ? {} : { timestampWrites: timestamp.value },
9150
9437
  colorAttachments: colors,
9151
9438
  ...depth?.kind === "texture-view" ? {
9152
9439
  depthStencilAttachment: {
9153
9440
  ...event.desc.depthStencilAttachment,
9154
9441
  view: depth.value
9155
9442
  }
9156
- } : {}
9443
+ } : {},
9444
+ ...querySet?.kind === "query-set" ? { occlusionQuerySet: querySet.value } : {}
9157
9445
  };
9158
9446
  const pass = encoder.value.beginRenderPass(descriptor);
9159
9447
  return context.table.set(
@@ -9162,6 +9450,24 @@ function beginRenderPass(context, event, eventIndex) {
9162
9450
  eventRecord(event)
9163
9451
  );
9164
9452
  }
9453
+ function replayTimestampWrites(context, event, eventIndex) {
9454
+ if (event.desc?.timestampWrites === void 0) return ok(void 0);
9455
+ const handle = event.timestampQuerySetHandleId;
9456
+ if (handle === void 0)
9457
+ return missingResource(eventIndex, event, "timestamp-query-set", "query-set");
9458
+ const query = requireResource(
9459
+ context,
9460
+ handle,
9461
+ "query-set",
9462
+ eventIndex,
9463
+ event.kind
9464
+ );
9465
+ if (!query.ok) return query;
9466
+ return ok({
9467
+ ...event.desc.timestampWrites,
9468
+ querySet: query.value
9469
+ });
9470
+ }
9165
9471
  function beginComputePass(context, event, eventIndex) {
9166
9472
  const encoder = requireResource(
9167
9473
  context,
@@ -9172,7 +9478,12 @@ function beginComputePass(context, event, eventIndex) {
9172
9478
  "command"
9173
9479
  );
9174
9480
  if (!encoder.ok) return encoder;
9175
- const pass = encoder.value.beginComputePass(JSON.parse(JSON.stringify(event.desc)));
9481
+ const timestamp = replayTimestampWrites(context, event, eventIndex);
9482
+ if (!timestamp.ok) return timestamp;
9483
+ const pass = encoder.value.beginComputePass({
9484
+ ...event.desc?.label === void 0 ? {} : { label: event.desc.label },
9485
+ ...timestamp.value === void 0 ? {} : { timestampWrites: timestamp.value }
9486
+ });
9176
9487
  return context.table.set(
9177
9488
  event.passHandleId,
9178
9489
  { kind: "encoder", role: "compute-pass", value: pass },
@@ -9353,7 +9664,7 @@ async function readReplayResource(device, table, resourceId, subresource, create
9353
9664
  });
9354
9665
  }
9355
9666
  function resolveTextureViewSubresource(view, source, requested) {
9356
- if (requested === void 0 || isBufferRange(requested)) return ok(requested);
9667
+ if (isBufferRange(requested)) return ok(requested);
9357
9668
  const sourceDescriptor = recordField(source.descriptor, "desc");
9358
9669
  const sourceSize = textureSize(sourceDescriptor?.size);
9359
9670
  const sourceMipCount = numberField(sourceDescriptor, "mipLevelCount") ?? 1;
@@ -9362,15 +9673,16 @@ function resolveTextureViewSubresource(view, source, requested) {
9362
9673
  const baseArrayLayer = numberField(viewDescriptor, "baseArrayLayer") ?? 0;
9363
9674
  const mipLevelCount = numberField(viewDescriptor, "mipLevelCount") ?? sourceMipCount - baseMipLevel;
9364
9675
  const arrayLayerCount = numberField(viewDescriptor, "arrayLayerCount") ?? sourceSize.depthOrArrayLayers - baseArrayLayer;
9365
- const localMipLevel = requested.mipLevel ?? 0;
9366
- const localArrayLayer = requested.arrayLayer ?? 0;
9676
+ const localMipLevel = requested?.mipLevel ?? 0;
9677
+ const localArrayLayer = requested?.arrayLayer ?? 0;
9367
9678
  if (!validIndex(baseMipLevel, sourceMipCount + 1) || !validIndex(baseArrayLayer, sourceSize.depthOrArrayLayers + 1) || !validIndex(localMipLevel, mipLevelCount) || !validIndex(localArrayLayer, arrayLayerCount) || baseMipLevel + mipLevelCount > sourceMipCount || baseArrayLayer + arrayLayerCount > sourceSize.depthOrArrayLayers) {
9368
9679
  return readbackFailure("texture view subresource is outside the recorded view extent");
9369
9680
  }
9370
9681
  return ok({
9371
9682
  ...requested,
9372
9683
  mipLevel: baseMipLevel + localMipLevel,
9373
- arrayLayer: baseArrayLayer + localArrayLayer
9684
+ arrayLayer: baseArrayLayer + localArrayLayer,
9685
+ aspect: requested?.aspect ?? (viewDescriptor?.aspect === "depth-only" || viewDescriptor?.aspect === "stencil-only" ? viewDescriptor.aspect : "all")
9374
9686
  });
9375
9687
  }
9376
9688
  async function readBuffer(device, entry, resourceId, subresource) {
@@ -9398,6 +9710,13 @@ async function readTexture(device, entry, resourceId, subresource, createShaderM
9398
9710
  const format = stringField2(descriptor, "format");
9399
9711
  const dimension = stringField2(descriptor, "dimension") ?? "2d";
9400
9712
  if (format === void 0) return readbackFailure(`texture ${resourceId} has no recorded format`);
9713
+ if ((numberField(descriptor, "sampleCount") ?? 1) > 1) {
9714
+ return readbackUnsupported(
9715
+ resourceId,
9716
+ format,
9717
+ "multisampled textures require a single-sample resolve target before readback"
9718
+ );
9719
+ }
9401
9720
  const plan = getTextureReadbackPlan({ format, dimension });
9402
9721
  if (!plan.supported) {
9403
9722
  return err(
@@ -9877,6 +10196,10 @@ var ResourceTable = class {
9877
10196
  const result = this.device.destroyTexture(entry.resource.value);
9878
10197
  if (!result.ok && firstFailure === void 0)
9879
10198
  firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
10199
+ } else if (entry.resource.kind === "query-set") {
10200
+ const result = this.device.destroyQuerySet(entry.resource.value);
10201
+ if (!result.ok && firstFailure === void 0)
10202
+ firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
9880
10203
  }
9881
10204
  }
9882
10205
  return firstFailure ?? ok(void 0);
@@ -10009,6 +10332,42 @@ async function openReplay(tape, backend) {
10009
10332
  backend.createShaderModule
10010
10333
  );
10011
10334
  },
10335
+ async readResourceAtWork(resourceId, workIndex, subresource, signal) {
10336
+ if (disposed) return positionError(workIndex, index.works.length);
10337
+ if (signal?.aborted) {
10338
+ return err(
10339
+ createRhiDebugError("readback-failed", {
10340
+ stage: "readback",
10341
+ cause: "readback was aborted"
10342
+ })
10343
+ );
10344
+ }
10345
+ const work = index.works[workIndex];
10346
+ if (work === void 0 || model.works[workIndex] === void 0) {
10347
+ return positionError(workIndex, index.works.length);
10348
+ }
10349
+ const cleared = await reset();
10350
+ if (!cleared.ok) return cleared;
10351
+ const bootstrapped = await prepare();
10352
+ if (!bootstrapped.ok) return bootstrapped;
10353
+ const replayed = await replayThroughWork(context, index, work, signal);
10354
+ if (!replayed.ok) return replayed;
10355
+ const read = await readReplayResource(
10356
+ backend.device,
10357
+ table,
10358
+ resourceId,
10359
+ subresource,
10360
+ backend.createShaderModule
10361
+ );
10362
+ if (!read.ok) return read;
10363
+ return ok({
10364
+ ...read.value,
10365
+ provenance: {
10366
+ ...read.value.provenance,
10367
+ selectedWorkIndex: work.workIndex
10368
+ }
10369
+ });
10370
+ },
10012
10371
  async dispose() {
10013
10372
  if (disposed) return ok(void 0);
10014
10373
  const result = table.dispose();
@@ -10110,7 +10469,8 @@ function requiredReplayCapabilities(tape) {
10110
10469
  }
10111
10470
  async function executeBootstrapResource(context, resource, bootstrapIndex) {
10112
10471
  const event = JSON.parse(JSON.stringify(resource.create));
10113
- const result = await executeEvent(context, event, -bootstrapIndex - 1);
10472
+ const replayEvent = event.kind === "createBuffer" && resource.initialData.length > 0 ? { ...event, desc: { ...event.desc, mappedAtCreation: true } } : event;
10473
+ const result = await executeEvent(context, replayEvent, -bootstrapIndex - 1);
10114
10474
  return result;
10115
10475
  }
10116
10476
  async function seedBootstrapResource(context, resource, bootstrapIndex) {
@@ -10128,25 +10488,41 @@ async function seedBootstrapResource(context, resource, bootstrapIndex) {
10128
10488
  "bootstrap initialData requires a buffer or color texture resource"
10129
10489
  );
10130
10490
  }
10131
- for (const slice of resource.initialData) {
10132
- const blob2 = context.tape.blobs.find((candidate) => candidate.hash === slice.hash);
10133
- if (blob2 === void 0) {
10134
- return eventFailure(-bootstrapIndex - 1, event, "lookup", `blob ${slice.hash} is missing`);
10135
- }
10136
- const end = slice.byteOffset + slice.byteLength;
10137
- if (slice.byteOffset < 0 || slice.byteLength < 0 || end > blob2.bytes.byteLength) {
10138
- return eventFailure(
10139
- -bootstrapIndex - 1,
10140
- event,
10141
- "lookup",
10142
- `blob ${slice.hash} does not contain initialData slice [${slice.byteOffset}, ${end})`
10143
- );
10144
- }
10145
- const bytes = blob2.bytes.slice(slice.byteOffset, slice.byteOffset + slice.byteLength);
10146
- const written = context.queue.writeBuffer(entry.resource.value, 0, bytes);
10147
- if (!written.ok) {
10148
- return eventFailure(-bootstrapIndex - 1, event, "write", written.error);
10491
+ const mapped = entry.resource.value;
10492
+ if (typeof mapped.getMappedRange !== "function" || typeof mapped.unmap !== "function") {
10493
+ if (context.device.caps.backendKind === "null") return ok(void 0);
10494
+ return eventFailure(
10495
+ -bootstrapIndex - 1,
10496
+ event,
10497
+ "write",
10498
+ "bootstrap buffer mapping is unavailable on the replay backend"
10499
+ );
10500
+ }
10501
+ try {
10502
+ const range = mapped.getMappedRange();
10503
+ if (!range.ok) return eventFailure(-bootstrapIndex - 1, event, "write", range.error);
10504
+ const target = new Uint8Array(range.value);
10505
+ for (const slice of resource.initialData) {
10506
+ const blob2 = context.tape.blobs.find((candidate) => candidate.hash === slice.hash);
10507
+ if (blob2 === void 0) {
10508
+ return eventFailure(-bootstrapIndex - 1, event, "lookup", `blob ${slice.hash} is missing`);
10509
+ }
10510
+ const end = slice.byteOffset + slice.byteLength;
10511
+ if (slice.byteOffset < 0 || slice.byteLength < 0 || end > blob2.bytes.byteLength) {
10512
+ return eventFailure(
10513
+ -bootstrapIndex - 1,
10514
+ event,
10515
+ "lookup",
10516
+ `blob ${slice.hash} does not contain initialData slice [${slice.byteOffset}, ${end})`
10517
+ );
10518
+ }
10519
+ if (slice.byteLength > target.byteLength) {
10520
+ return eventFailure(-bootstrapIndex - 1, event, "write", "initialData exceeds buffer size");
10521
+ }
10522
+ target.set(blob2.bytes.subarray(slice.byteOffset, end));
10149
10523
  }
10524
+ } finally {
10525
+ mapped.unmap();
10150
10526
  }
10151
10527
  return ok(void 0);
10152
10528
  }
@@ -10245,12 +10621,8 @@ async function finalizeWorkPass(context, index, work) {
10245
10621
  `pass ${begin.passHandleId} is not open`
10246
10622
  );
10247
10623
  }
10248
- try {
10249
- entry.resource.value.end();
10250
- } catch (cause) {
10251
- return eventFailure(pass.endEventIndex ?? work.eventIndex, begin, "encode", cause);
10252
- }
10253
- context.table.delete(begin.passHandleId);
10624
+ const closed = closeReplayPass(context, entry, begin, pass, work.eventIndex);
10625
+ if (!closed.ok) return closed;
10254
10626
  const finishEventIndex = findNextEvent(
10255
10627
  context.tape.events,
10256
10628
  pass.beginEventIndex,
@@ -10266,6 +10638,13 @@ async function finalizeWorkPass(context, index, work) {
10266
10638
  `encoder ${begin.cmdHandleId} is not available`
10267
10639
  );
10268
10640
  }
10641
+ const closure = await replayCommandClosure(
10642
+ context,
10643
+ begin.cmdHandleId,
10644
+ (pass.endEventIndex ?? work.eventIndex) + 1,
10645
+ finishEventIndex ?? context.tape.events.length
10646
+ );
10647
+ if (!closure.ok) return closure;
10269
10648
  const finished = encoder.resource.value.finish();
10270
10649
  const finishEvent = context.tape.events[finishEventIndex ?? work.eventIndex] ?? begin;
10271
10650
  if (!finished.ok)
@@ -10293,6 +10672,95 @@ async function finalizeWorkPass(context, index, work) {
10293
10672
  await context.queue.onSubmittedWorkDone();
10294
10673
  return ok(void 0);
10295
10674
  }
10675
+ function closeReplayPass(context, entry, begin, pass, selectedEventIndex) {
10676
+ const activeQuery = activeOcclusionQuery(
10677
+ context.tape.events,
10678
+ begin.kind === "beginRenderPass" ? begin.passHandleId : void 0,
10679
+ pass.beginEventIndex,
10680
+ selectedEventIndex
10681
+ );
10682
+ if (!activeQuery.ok) return activeQuery;
10683
+ try {
10684
+ if (activeQuery.value !== void 0) {
10685
+ if (entry.resource.role !== "render-pass") {
10686
+ return eventFailure(
10687
+ activeQuery.value.eventIndex,
10688
+ activeQuery.value.event,
10689
+ "lookup",
10690
+ "an active occlusion query belongs to a non-render pass"
10691
+ );
10692
+ }
10693
+ const endedQuery = entry.resource.value.endOcclusionQuery();
10694
+ if (!endedQuery.ok) {
10695
+ return eventFailure(
10696
+ activeQuery.value.eventIndex,
10697
+ activeQuery.value.event,
10698
+ "encode",
10699
+ endedQuery.error
10700
+ );
10701
+ }
10702
+ }
10703
+ entry.resource.value.end();
10704
+ } catch (cause) {
10705
+ return eventFailure(pass.endEventIndex ?? selectedEventIndex, begin, "encode", cause);
10706
+ }
10707
+ context.table.delete(begin.passHandleId);
10708
+ return ok(void 0);
10709
+ }
10710
+ async function replayCommandClosure(context, cmdHandleId, start, end) {
10711
+ for (let eventIndex = start; eventIndex < end; eventIndex++) {
10712
+ const event = context.tape.events[eventIndex];
10713
+ if (event === void 0 || !isCommandClosureEvent(event)) continue;
10714
+ if (event.cmdHandleId !== cmdHandleId) continue;
10715
+ const result = await executeEvent(context, event, eventIndex);
10716
+ if (!result.ok) return result;
10717
+ }
10718
+ return ok(void 0);
10719
+ }
10720
+ function activeOcclusionQuery(events, passHandleId, beginEventIndex, selectedEventIndex) {
10721
+ if (passHandleId === void 0) return ok(void 0);
10722
+ let active;
10723
+ for (let eventIndex = beginEventIndex + 1; eventIndex <= selectedEventIndex; eventIndex++) {
10724
+ const event = events[eventIndex];
10725
+ if (event === void 0 || !("passHandleId" in event) || event.passHandleId !== passHandleId)
10726
+ continue;
10727
+ if (event.kind === "beginOcclusionQuery") {
10728
+ if (active !== void 0) {
10729
+ return eventFailure(
10730
+ eventIndex,
10731
+ event,
10732
+ "encode",
10733
+ "an occlusion query is already active for this render pass"
10734
+ );
10735
+ }
10736
+ active = { eventIndex, event };
10737
+ } else if (event.kind === "endOcclusionQuery") {
10738
+ if (active === void 0) {
10739
+ return eventFailure(
10740
+ eventIndex,
10741
+ event,
10742
+ "encode",
10743
+ "an occlusion query ended without a matching begin"
10744
+ );
10745
+ }
10746
+ active = void 0;
10747
+ }
10748
+ }
10749
+ return ok(active);
10750
+ }
10751
+ function isCommandClosureEvent(event) {
10752
+ switch (event.kind) {
10753
+ case "resolveQuerySet":
10754
+ case "copyBufferToBuffer":
10755
+ case "copyBufferToTexture":
10756
+ case "copyTextureToBuffer":
10757
+ case "copyTextureToTexture":
10758
+ case "clearBuffer":
10759
+ return true;
10760
+ default:
10761
+ return false;
10762
+ }
10763
+ }
10296
10764
  function findNextEvent(events, start, kind, commandId) {
10297
10765
  for (let index = start + 1; index < events.length; index++) {
10298
10766
  const event = events[index];
@@ -10322,9 +10790,10 @@ async function readWorkAttachment(context, index, work) {
10322
10790
  })
10323
10791
  );
10324
10792
  }
10325
- const viewId = begin.colorAttachmentViewHandleIds.find(
10793
+ const attachmentIndex = begin.colorAttachmentViewHandleIds.findIndex(
10326
10794
  (candidate) => candidate !== void 0
10327
10795
  );
10796
+ const viewId = begin.colorAttachmentResolveTargetHandleIds?.[attachmentIndex] ?? begin.colorAttachmentViewHandleIds[attachmentIndex];
10328
10797
  if (viewId === void 0) {
10329
10798
  return err(
10330
10799
  createRhiDebugError("readback-unsupported", {