@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
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,8 @@ function referencedHandles(event) {
4422
4431
  "resourceHandleIds",
4423
4432
  "bindGroupHandleId",
4424
4433
  "pipelineHandleId",
4434
+ "querySetHandleId",
4435
+ "occlusionQuerySetHandleId",
4425
4436
  "indexBufferHandleId",
4426
4437
  "vertexBufferHandleId"
4427
4438
  ];
@@ -5256,6 +5267,7 @@ function isResourceKind(value) {
5256
5267
  return [
5257
5268
  "buffer",
5258
5269
  "texture",
5270
+ "query-set",
5259
5271
  "texture-view",
5260
5272
  "sampler",
5261
5273
  "shader-module",
@@ -5268,8 +5280,10 @@ function eventResources(event) {
5268
5280
  switch (event.kind) {
5269
5281
  case "createBuffer":
5270
5282
  case "createTexture":
5283
+ case "createQuerySet":
5271
5284
  case "createSampler":
5272
5285
  case "createBindGroupLayout":
5286
+ case "getBindGroupLayout":
5273
5287
  case "createBindGroup":
5274
5288
  case "createPipelineLayout":
5275
5289
  case "createRenderPipeline":
@@ -5279,6 +5293,7 @@ function eventResources(event) {
5279
5293
  created: [event.handleId],
5280
5294
  reads: handleRefs(event, [
5281
5295
  "layoutHandleId",
5296
+ "pipelineHandleId",
5282
5297
  "vertexShaderModuleHandleId",
5283
5298
  "fragmentShaderModuleHandleId",
5284
5299
  "computeShaderModuleHandleId"
@@ -5291,6 +5306,7 @@ function eventResources(event) {
5291
5306
  return { created: [event.cmdHandleId], reads: [], destroyed: [] };
5292
5307
  case "destroyBuffer":
5293
5308
  case "destroyTexture":
5309
+ case "destroyQuerySet":
5294
5310
  return { created: [], reads: [event.handleId], destroyed: [event.handleId] };
5295
5311
  case "writeBuffer":
5296
5312
  case "writeTexture":
@@ -5312,6 +5328,32 @@ function eventResources(event) {
5312
5328
  ]),
5313
5329
  destroyed: []
5314
5330
  };
5331
+ case "resolveQuerySet":
5332
+ return {
5333
+ created: [],
5334
+ reads: handleRefs(event, ["cmdHandleId", "querySetHandleId", "destinationHandleId"]),
5335
+ destroyed: []
5336
+ };
5337
+ case "beginRenderPass":
5338
+ return {
5339
+ created: [event.passHandleId],
5340
+ reads: handleRefs(event, [
5341
+ "cmdHandleId",
5342
+ "occlusionQuerySetHandleId",
5343
+ "colorAttachmentViewHandleIds",
5344
+ "colorAttachmentResolveTargetHandleIds",
5345
+ "depthStencilViewHandleId"
5346
+ ]),
5347
+ destroyed: []
5348
+ };
5349
+ case "beginComputePass":
5350
+ return { created: [event.passHandleId], reads: [event.cmdHandleId], destroyed: [] };
5351
+ case "endRenderPass":
5352
+ case "endComputePass":
5353
+ return { created: [], reads: [event.passHandleId], destroyed: [event.passHandleId] };
5354
+ case "beginOcclusionQuery":
5355
+ case "endOcclusionQuery":
5356
+ return { created: [], reads: handleRefs(event, ["passHandleId"]), destroyed: [] };
5315
5357
  default:
5316
5358
  return { created: [], reads: [], destroyed: [] };
5317
5359
  }
@@ -6098,7 +6140,8 @@ function createdHandleId(event) {
6098
6140
  if (event.kind === "beginRenderPass" || event.kind === "beginComputePass") {
6099
6141
  return event.passHandleId;
6100
6142
  }
6101
- if (event.kind.startsWith("create") && "handleId" in event) return event.handleId;
6143
+ if ((event.kind.startsWith("create") || event.kind === "getBindGroupLayout") && "handleId" in event)
6144
+ return event.handleId;
6102
6145
  return void 0;
6103
6146
  }
6104
6147
  function resourceKind(event) {
@@ -6107,6 +6150,8 @@ function resourceKind(event) {
6107
6150
  return "buffer";
6108
6151
  case "createTexture":
6109
6152
  return "texture";
6153
+ case "createQuerySet":
6154
+ return "query-set";
6110
6155
  case "createTextureView":
6111
6156
  return "texture-view";
6112
6157
  case "createSampler":
@@ -6118,6 +6163,7 @@ function resourceKind(event) {
6118
6163
  return "pipeline";
6119
6164
  case "createBindGroup":
6120
6165
  case "createBindGroupLayout":
6166
+ case "getBindGroupLayout":
6121
6167
  case "createPipelineLayout":
6122
6168
  return "binding";
6123
6169
  case "createCommandEncoder":
@@ -6263,6 +6309,7 @@ function _collectFrameReferencedHandleIds(events) {
6263
6309
  if (vhId !== void 0) refs.add(vhId);
6264
6310
  }
6265
6311
  if (we.depthStencilViewHandleId !== void 0) refs.add(we.depthStencilViewHandleId);
6312
+ if (we.occlusionQuerySetHandleId !== void 0) refs.add(we.occlusionQuerySetHandleId);
6266
6313
  break;
6267
6314
  }
6268
6315
  case "beginComputePass": {
@@ -6317,6 +6364,7 @@ function _collectFrameReferencedHandleIds(events) {
6317
6364
  break;
6318
6365
  }
6319
6366
  case "createBindGroup":
6367
+ case "getBindGroupLayout":
6320
6368
  case "createPipelineLayout":
6321
6369
  case "createRenderPipeline":
6322
6370
  case "createComputePipeline":
@@ -6326,8 +6374,27 @@ function _collectFrameReferencedHandleIds(events) {
6326
6374
  }
6327
6375
  case "destroyBuffer":
6328
6376
  case "destroyTexture":
6377
+ case "destroyQuerySet":
6329
6378
  refs.add(e.handleId);
6330
6379
  break;
6380
+ case "frameMark":
6381
+ case "createBuffer":
6382
+ case "createTexture":
6383
+ case "createQuerySet":
6384
+ case "createSampler":
6385
+ case "createBindGroupLayout":
6386
+ case "createShaderModule":
6387
+ case "createCommandEncoder":
6388
+ break;
6389
+ case "resolveQuerySet":
6390
+ refs.add(e.cmdHandleId);
6391
+ refs.add(e.querySetHandleId);
6392
+ refs.add(e.destinationHandleId);
6393
+ break;
6394
+ case "beginOcclusionQuery":
6395
+ case "endOcclusionQuery":
6396
+ refs.add(e.passHandleId);
6397
+ break;
6331
6398
  }
6332
6399
  }
6333
6400
  return refs;
@@ -6361,8 +6428,11 @@ function _getCreateEventReferencedHandleIds(event) {
6361
6428
  const e = event;
6362
6429
  return [e.sourceHandleId];
6363
6430
  }
6431
+ case "getBindGroupLayout":
6432
+ return [event.pipelineHandleId];
6364
6433
  case "createBuffer":
6365
6434
  case "createTexture":
6435
+ case "createQuerySet":
6366
6436
  case "createSampler":
6367
6437
  case "createBindGroupLayout":
6368
6438
  case "createShaderModule":
@@ -6608,18 +6678,21 @@ function getHandleId(s, handle, kind) {
6608
6678
  }
6609
6679
 
6610
6680
  // src/recorder/shader.ts
6681
+ function recordShaderModule(debugInst, result, desc) {
6682
+ if (!result.ok) return result;
6683
+ const hId = debugInst.pushExternalCreateEvent(result.value, "shaderModule", {
6684
+ kind: "createShaderModule",
6685
+ handleId: "",
6686
+ wgslCode: desc.code
6687
+ });
6688
+ debugInst.registerShaderModule(result.value, hId);
6689
+ return result;
6690
+ }
6611
6691
  function wrapCreateShaderModule(originalFn, debugInst) {
6612
6692
  return async function wrappedCreateShaderModule(device, desc) {
6613
6693
  const realDevice = device._realDevice ?? device;
6614
6694
  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;
6695
+ return recordShaderModule(debugInst, result, desc);
6623
6696
  };
6624
6697
  }
6625
6698
 
@@ -6782,10 +6855,15 @@ function createRenderPassProxy(s, realPass, passHId) {
6782
6855
  return realPass.executeBundles(bundles);
6783
6856
  },
6784
6857
  beginOcclusionQuery(queryIndex) {
6785
- return realPass.beginOcclusionQuery(queryIndex);
6858
+ const result = realPass.beginOcclusionQuery(queryIndex);
6859
+ if (result.ok)
6860
+ pushEvent(s, { kind: "beginOcclusionQuery", passHandleId: passHId, queryIndex });
6861
+ return result;
6786
6862
  },
6787
6863
  endOcclusionQuery() {
6788
- return realPass.endOcclusionQuery();
6864
+ const result = realPass.endOcclusionQuery();
6865
+ if (result.ok) pushEvent(s, { kind: "endOcclusionQuery", passHandleId: passHId });
6866
+ return result;
6789
6867
  },
6790
6868
  end() {
6791
6869
  pushEvent(s, { kind: "endRenderPass", passHandleId: passHId });
@@ -6875,13 +6953,19 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
6875
6953
  (attachment) => attachment === null || attachment === void 0 ? attachment : { ...attachment }
6876
6954
  ),
6877
6955
  ...desc.depthStencilAttachment === void 0 ? {} : { depthStencilAttachment: { ...desc.depthStencilAttachment } },
6878
- ...desc.occlusionQuerySet === void 0 ? {} : { occlusionQuerySet: desc.occlusionQuerySet },
6879
6956
  ...desc.timestampWrites === void 0 ? {} : { timestampWrites: desc.timestampWrites },
6880
6957
  ...desc.maxDrawCount === void 0 ? {} : { maxDrawCount: desc.maxDrawCount }
6881
6958
  },
6882
6959
  colorAttachmentViewHandleIds,
6883
6960
  colorAttachmentResolveTargetHandleIds,
6884
- depthStencilViewHandleId
6961
+ depthStencilViewHandleId,
6962
+ ...desc.occlusionQuerySet === void 0 ? {} : {
6963
+ occlusionQuerySetHandleId: getHandleId(
6964
+ s,
6965
+ desc.occlusionQuerySet,
6966
+ "querySet"
6967
+ )
6968
+ }
6885
6969
  });
6886
6970
  const realPass = realEnc.beginRenderPass(desc);
6887
6971
  return createRenderPassProxy(s, realPass, passHId);
@@ -7021,13 +7105,25 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
7021
7105
  realEnc.clearBuffer(buffer, offset, size);
7022
7106
  },
7023
7107
  resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset) {
7024
- return realEnc.resolveQuerySet(
7108
+ const result = realEnc.resolveQuerySet(
7025
7109
  querySet,
7026
7110
  firstQuery,
7027
7111
  queryCount,
7028
7112
  destination,
7029
7113
  destinationOffset
7030
7114
  );
7115
+ if (result.ok) {
7116
+ pushEvent(s, {
7117
+ kind: "resolveQuerySet",
7118
+ cmdHandleId: cmdHId,
7119
+ querySetHandleId: getHandleId(s, querySet, "querySet"),
7120
+ firstQuery,
7121
+ queryCount,
7122
+ destinationHandleId: getHandleId(s, destination, "buffer"),
7123
+ destinationOffset
7124
+ });
7125
+ }
7126
+ return result;
7031
7127
  },
7032
7128
  pushDebugGroup(groupLabel) {
7033
7129
  pushEvent(s, { kind: "pushDebugGroup", cmdHandleId: cmdHId, groupLabel });
@@ -7052,6 +7148,29 @@ function createCommandEncoderProxy(s, realEnc, cmdHId) {
7052
7148
  };
7053
7149
  }
7054
7150
 
7151
+ // src/recorder/pipeline.ts
7152
+ function wrapPipeline(state, pipeline, pipelineHandleId) {
7153
+ const operation = pipeline.getBindGroupLayout;
7154
+ if (operation === void 0) return pipeline;
7155
+ const getLayout = operation.bind(pipeline);
7156
+ Object.defineProperty(pipeline, "getBindGroupLayout", {
7157
+ configurable: true,
7158
+ value: (index) => {
7159
+ const layout = getLayout(index);
7160
+ const event = {
7161
+ kind: "getBindGroupLayout",
7162
+ handleId: "",
7163
+ pipelineHandleId,
7164
+ index
7165
+ };
7166
+ registerHandle(state, layout, "bindGroupLayout", event);
7167
+ pushEvent(state, event);
7168
+ return layout;
7169
+ }
7170
+ });
7171
+ return pipeline;
7172
+ }
7173
+
7055
7174
  // src/recorder/queue.ts
7056
7175
  function createQueueProxy(s, realQueue) {
7057
7176
  return {
@@ -7381,9 +7500,9 @@ function createDeviceProxy(s, realDevice) {
7381
7500
  vertexShaderModuleHandleId,
7382
7501
  fragmentShaderModuleHandleId
7383
7502
  };
7384
- registerHandle(s, res.value, "renderPipeline", event);
7503
+ const pipelineId = registerHandle(s, res.value, "renderPipeline", event);
7385
7504
  pushEvent(s, event);
7386
- return res;
7505
+ return ok(wrapPipeline(s, res.value, pipelineId));
7387
7506
  },
7388
7507
  createComputePipeline(desc) {
7389
7508
  const res = realDevice.createComputePipeline(desc);
@@ -7406,12 +7525,25 @@ function createDeviceProxy(s, realDevice) {
7406
7525
  layoutHandleId: layoutId,
7407
7526
  computeShaderModuleHandleId
7408
7527
  };
7409
- registerHandle(s, res.value, "computePipeline", event);
7528
+ const pipelineId = registerHandle(s, res.value, "computePipeline", event);
7410
7529
  pushEvent(s, event);
7411
- return res;
7530
+ return ok(wrapPipeline(s, res.value, pipelineId));
7412
7531
  },
7413
7532
  createQuerySet(desc) {
7414
- return realDevice.createQuerySet(desc);
7533
+ const res = realDevice.createQuerySet(desc);
7534
+ if (!res.ok) return res;
7535
+ const event = {
7536
+ kind: "createQuerySet",
7537
+ handleId: "",
7538
+ desc: {
7539
+ ...desc.label === void 0 ? {} : { label: desc.label },
7540
+ type: desc.type ?? "occlusion",
7541
+ count: desc.count ?? 0
7542
+ }
7543
+ };
7544
+ registerHandle(s, res.value, "querySet", event);
7545
+ pushEvent(s, event);
7546
+ return res;
7415
7547
  },
7416
7548
  destroyBuffer(buf) {
7417
7549
  const hId = s.handleMap.get(buf);
@@ -7427,7 +7559,16 @@ function createDeviceProxy(s, realDevice) {
7427
7559
  return res;
7428
7560
  },
7429
7561
  destroyQuerySet(querySet) {
7430
- return realDevice.destroyQuerySet(querySet);
7562
+ const hId = s.handleMap.get(querySet);
7563
+ const res = realDevice.destroyQuerySet(querySet);
7564
+ if (!res.ok) return res;
7565
+ if (hId !== void 0) {
7566
+ if (!retainsCaptureBootstrap(s) && !s.snapshotSeededHandles.has(hId) && !hasBootstrapDependency(s, hId)) {
7567
+ s.bootstrapCreates.delete(hId);
7568
+ }
7569
+ pushEvent(s, { kind: "destroyQuerySet", handleId: hId });
7570
+ }
7571
+ return res;
7431
7572
  },
7432
7573
  destroyTexture(tex) {
7433
7574
  const hId = s.handleMap.get(tex);
@@ -8180,15 +8321,46 @@ function wrap(instance) {
8180
8321
  function createRecorderProxy(backend) {
8181
8322
  const recorder = wrap(backend.rhi);
8182
8323
  const wrappedCreateShaderModule = wrapCreateShaderModule(backend.createShaderModule, recorder);
8324
+ const originalCreateShaderModuleImmediate = backend.createShaderModuleImmediate;
8325
+ const wrappedCreateShaderModuleImmediate = originalCreateShaderModuleImmediate === void 0 ? void 0 : (device, desc) => {
8326
+ const realDevice = device._realDevice ?? device;
8327
+ const result = originalCreateShaderModuleImmediate(realDevice, desc);
8328
+ if (!result.ok) return result;
8329
+ const hId = recorder.pushExternalCreateEvent(result.value, "shaderModule", {
8330
+ kind: "createShaderModule",
8331
+ handleId: "",
8332
+ wgslCode: desc.code
8333
+ });
8334
+ recorder.registerShaderModule(result.value, hId);
8335
+ return result;
8336
+ };
8183
8337
  const acquireCanvasContext = backend.rhi.acquireCanvasContext;
8184
8338
  const rhi = {
8185
8339
  requestAdapter: recorder.requestAdapter.bind(recorder),
8186
8340
  createShaderModule: wrappedCreateShaderModule,
8187
- ...acquireCanvasContext === void 0 ? {} : { acquireCanvasContext: acquireCanvasContext.bind(backend.rhi) }
8341
+ ...wrappedCreateShaderModuleImmediate === void 0 ? {} : { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate },
8342
+ ...acquireCanvasContext === void 0 ? {} : {
8343
+ acquireCanvasContext(canvas) {
8344
+ const result = acquireCanvasContext.call(backend.rhi, canvas);
8345
+ if (!result.ok) return result;
8346
+ const context = result.value;
8347
+ return ok({
8348
+ ...context,
8349
+ configure(configuration) {
8350
+ const device = configuration.device._realDevice;
8351
+ return context.configure({
8352
+ ...configuration,
8353
+ ...device === void 0 ? {} : { device }
8354
+ });
8355
+ }
8356
+ });
8357
+ }
8358
+ }
8188
8359
  };
8189
8360
  const wrappedBackend = {
8190
8361
  rhi,
8191
8362
  createShaderModule: wrappedCreateShaderModule,
8363
+ ...wrappedCreateShaderModuleImmediate === void 0 ? {} : { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate },
8192
8364
  unwrapDeviceForSurface(device) {
8193
8365
  const raw = device._realDevice;
8194
8366
  if (raw !== void 0) return ok(raw);
@@ -8619,10 +8791,20 @@ async function executeEvent(context, event, eventIndex) {
8619
8791
  kind: "texture"
8620
8792
  }
8621
8793
  );
8794
+ case "createQuerySet":
8795
+ return createResource(
8796
+ context,
8797
+ eventIndex,
8798
+ event,
8799
+ context.device.createQuerySet(event.desc),
8800
+ { kind: "query-set" }
8801
+ );
8622
8802
  case "destroyBuffer":
8623
8803
  return destroyResource(context, eventIndex, event, "buffer");
8624
8804
  case "destroyTexture":
8625
8805
  return destroyResource(context, eventIndex, event, "texture");
8806
+ case "destroyQuerySet":
8807
+ return destroyQuerySet(context, event, eventIndex);
8626
8808
  case "createTextureView": {
8627
8809
  const texture = requireResource(
8628
8810
  context,
@@ -8659,6 +8841,25 @@ async function executeEvent(context, event, eventIndex) {
8659
8841
  context.device.createBindGroupLayout(event.desc),
8660
8842
  { kind: "binding", role: "bind-group-layout" }
8661
8843
  );
8844
+ case "getBindGroupLayout": {
8845
+ const pipeline = requireReplayResource(
8846
+ context,
8847
+ event.pipelineHandleId,
8848
+ "pipeline",
8849
+ eventIndex,
8850
+ event.kind
8851
+ );
8852
+ if (!pipeline.ok) return pipeline;
8853
+ return createResource(
8854
+ context,
8855
+ eventIndex,
8856
+ event,
8857
+ ok(
8858
+ pipeline.value.value.getBindGroupLayout(event.index)
8859
+ ),
8860
+ { kind: "binding", role: "bind-group-layout" }
8861
+ );
8862
+ }
8662
8863
  case "createBindGroup":
8663
8864
  return createBindGroup(context, event, eventIndex);
8664
8865
  case "createPipelineLayout":
@@ -8690,6 +8891,18 @@ async function executeEvent(context, event, eventIndex) {
8690
8891
  return beginRenderPass(context, event, eventIndex);
8691
8892
  case "beginComputePass":
8692
8893
  return beginComputePass(context, event, eventIndex);
8894
+ case "resolveQuerySet":
8895
+ return resolveQuerySet(context, event, eventIndex);
8896
+ case "beginOcclusionQuery":
8897
+ return passCall(context, event, eventIndex, "render-pass", (pass) => {
8898
+ const result = pass.beginOcclusionQuery(event.queryIndex);
8899
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
8900
+ });
8901
+ case "endOcclusionQuery":
8902
+ return passCall(context, event, eventIndex, "render-pass", (pass) => {
8903
+ const result = pass.endOcclusionQuery();
8904
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
8905
+ });
8693
8906
  case "copyBufferToBuffer":
8694
8907
  return copyBufferToBuffer(context, event, eventIndex);
8695
8908
  case "copyBufferToTexture":
@@ -8920,6 +9133,50 @@ function destroyResource(context, eventIndex, event, kind) {
8920
9133
  context.table.delete(event.handleId);
8921
9134
  return ok(void 0);
8922
9135
  }
9136
+ function destroyQuerySet(context, event, eventIndex) {
9137
+ const entry = context.table.get(event.handleId);
9138
+ if (entry?.resource.kind !== "query-set")
9139
+ return missingResource(eventIndex, event, event.handleId, "query-set");
9140
+ const result = context.device.destroyQuerySet(entry.resource.value);
9141
+ if (!result.ok) return eventFailure(eventIndex, event, "create", result.error);
9142
+ context.table.delete(event.handleId);
9143
+ return ok(void 0);
9144
+ }
9145
+ function resolveQuerySet(context, event, eventIndex) {
9146
+ const encoder = requireResource(
9147
+ context,
9148
+ event.cmdHandleId,
9149
+ "encoder",
9150
+ eventIndex,
9151
+ event.kind,
9152
+ "command"
9153
+ );
9154
+ const querySet = requireResource(
9155
+ context,
9156
+ event.querySetHandleId,
9157
+ "query-set",
9158
+ eventIndex,
9159
+ event.kind
9160
+ );
9161
+ const destination = requireResource(
9162
+ context,
9163
+ event.destinationHandleId,
9164
+ "buffer",
9165
+ eventIndex,
9166
+ event.kind
9167
+ );
9168
+ if (!encoder.ok) return encoder;
9169
+ if (!querySet.ok) return querySet;
9170
+ if (!destination.ok) return destination;
9171
+ const result = encoder.value.resolveQuerySet(
9172
+ querySet.value,
9173
+ event.firstQuery,
9174
+ event.queryCount,
9175
+ destination.value,
9176
+ event.destinationOffset
9177
+ );
9178
+ return result.ok ? ok(void 0) : eventFailure(eventIndex, event, "encode", result.error);
9179
+ }
8923
9180
  async function createShaderModule(context, event, eventIndex) {
8924
9181
  const result = await context.createShaderModule(context.device, { code: event.wgslCode });
8925
9182
  return createResource(context, eventIndex, event, result, { kind: "shader-module" });
@@ -9145,6 +9402,10 @@ function beginRenderPass(context, event, eventIndex) {
9145
9402
  };
9146
9403
  });
9147
9404
  const depth = event.depthStencilViewHandleId === void 0 ? void 0 : context.table.get(event.depthStencilViewHandleId)?.resource;
9405
+ const querySet = event.occlusionQuerySetHandleId === void 0 ? void 0 : context.table.get(event.occlusionQuerySetHandleId)?.resource;
9406
+ if (event.occlusionQuerySetHandleId !== void 0 && querySet?.kind !== "query-set") {
9407
+ return missingResource(eventIndex, event, event.occlusionQuerySetHandleId, "query-set");
9408
+ }
9148
9409
  const descriptor = {
9149
9410
  ...event.desc,
9150
9411
  colorAttachments: colors,
@@ -9153,7 +9414,8 @@ function beginRenderPass(context, event, eventIndex) {
9153
9414
  ...event.desc.depthStencilAttachment,
9154
9415
  view: depth.value
9155
9416
  }
9156
- } : {}
9417
+ } : {},
9418
+ ...querySet?.kind === "query-set" ? { occlusionQuerySet: querySet.value } : {}
9157
9419
  };
9158
9420
  const pass = encoder.value.beginRenderPass(descriptor);
9159
9421
  return context.table.set(
@@ -9353,7 +9615,7 @@ async function readReplayResource(device, table, resourceId, subresource, create
9353
9615
  });
9354
9616
  }
9355
9617
  function resolveTextureViewSubresource(view, source, requested) {
9356
- if (requested === void 0 || isBufferRange(requested)) return ok(requested);
9618
+ if (isBufferRange(requested)) return ok(requested);
9357
9619
  const sourceDescriptor = recordField(source.descriptor, "desc");
9358
9620
  const sourceSize = textureSize(sourceDescriptor?.size);
9359
9621
  const sourceMipCount = numberField(sourceDescriptor, "mipLevelCount") ?? 1;
@@ -9362,15 +9624,16 @@ function resolveTextureViewSubresource(view, source, requested) {
9362
9624
  const baseArrayLayer = numberField(viewDescriptor, "baseArrayLayer") ?? 0;
9363
9625
  const mipLevelCount = numberField(viewDescriptor, "mipLevelCount") ?? sourceMipCount - baseMipLevel;
9364
9626
  const arrayLayerCount = numberField(viewDescriptor, "arrayLayerCount") ?? sourceSize.depthOrArrayLayers - baseArrayLayer;
9365
- const localMipLevel = requested.mipLevel ?? 0;
9366
- const localArrayLayer = requested.arrayLayer ?? 0;
9627
+ const localMipLevel = requested?.mipLevel ?? 0;
9628
+ const localArrayLayer = requested?.arrayLayer ?? 0;
9367
9629
  if (!validIndex(baseMipLevel, sourceMipCount + 1) || !validIndex(baseArrayLayer, sourceSize.depthOrArrayLayers + 1) || !validIndex(localMipLevel, mipLevelCount) || !validIndex(localArrayLayer, arrayLayerCount) || baseMipLevel + mipLevelCount > sourceMipCount || baseArrayLayer + arrayLayerCount > sourceSize.depthOrArrayLayers) {
9368
9630
  return readbackFailure("texture view subresource is outside the recorded view extent");
9369
9631
  }
9370
9632
  return ok({
9371
9633
  ...requested,
9372
9634
  mipLevel: baseMipLevel + localMipLevel,
9373
- arrayLayer: baseArrayLayer + localArrayLayer
9635
+ arrayLayer: baseArrayLayer + localArrayLayer,
9636
+ aspect: requested?.aspect ?? (viewDescriptor?.aspect === "depth-only" || viewDescriptor?.aspect === "stencil-only" ? viewDescriptor.aspect : "all")
9374
9637
  });
9375
9638
  }
9376
9639
  async function readBuffer(device, entry, resourceId, subresource) {
@@ -9877,6 +10140,10 @@ var ResourceTable = class {
9877
10140
  const result = this.device.destroyTexture(entry.resource.value);
9878
10141
  if (!result.ok && firstFailure === void 0)
9879
10142
  firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
10143
+ } else if (entry.resource.kind === "query-set") {
10144
+ const result = this.device.destroyQuerySet(entry.resource.value);
10145
+ if (!result.ok && firstFailure === void 0)
10146
+ firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
9880
10147
  }
9881
10148
  }
9882
10149
  return firstFailure ?? ok(void 0);
@@ -10009,6 +10276,42 @@ async function openReplay(tape, backend) {
10009
10276
  backend.createShaderModule
10010
10277
  );
10011
10278
  },
10279
+ async readResourceAtWork(resourceId, workIndex, subresource, signal) {
10280
+ if (disposed) return positionError(workIndex, index.works.length);
10281
+ if (signal?.aborted) {
10282
+ return err(
10283
+ createRhiDebugError("readback-failed", {
10284
+ stage: "readback",
10285
+ cause: "readback was aborted"
10286
+ })
10287
+ );
10288
+ }
10289
+ const work = index.works[workIndex];
10290
+ if (work === void 0 || model.works[workIndex] === void 0) {
10291
+ return positionError(workIndex, index.works.length);
10292
+ }
10293
+ const cleared = await reset();
10294
+ if (!cleared.ok) return cleared;
10295
+ const bootstrapped = await prepare();
10296
+ if (!bootstrapped.ok) return bootstrapped;
10297
+ const replayed = await replayThroughWork(context, index, work, signal);
10298
+ if (!replayed.ok) return replayed;
10299
+ const read = await readReplayResource(
10300
+ backend.device,
10301
+ table,
10302
+ resourceId,
10303
+ subresource,
10304
+ backend.createShaderModule
10305
+ );
10306
+ if (!read.ok) return read;
10307
+ return ok({
10308
+ ...read.value,
10309
+ provenance: {
10310
+ ...read.value.provenance,
10311
+ selectedWorkIndex: work.workIndex
10312
+ }
10313
+ });
10314
+ },
10012
10315
  async dispose() {
10013
10316
  if (disposed) return ok(void 0);
10014
10317
  const result = table.dispose();
@@ -10110,7 +10413,8 @@ function requiredReplayCapabilities(tape) {
10110
10413
  }
10111
10414
  async function executeBootstrapResource(context, resource, bootstrapIndex) {
10112
10415
  const event = JSON.parse(JSON.stringify(resource.create));
10113
- const result = await executeEvent(context, event, -bootstrapIndex - 1);
10416
+ const replayEvent = event.kind === "createBuffer" && resource.initialData.length > 0 ? { ...event, desc: { ...event.desc, mappedAtCreation: true } } : event;
10417
+ const result = await executeEvent(context, replayEvent, -bootstrapIndex - 1);
10114
10418
  return result;
10115
10419
  }
10116
10420
  async function seedBootstrapResource(context, resource, bootstrapIndex) {
@@ -10128,25 +10432,41 @@ async function seedBootstrapResource(context, resource, bootstrapIndex) {
10128
10432
  "bootstrap initialData requires a buffer or color texture resource"
10129
10433
  );
10130
10434
  }
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);
10435
+ const mapped = entry.resource.value;
10436
+ if (typeof mapped.getMappedRange !== "function" || typeof mapped.unmap !== "function") {
10437
+ if (context.device.caps.backendKind === "null") return ok(void 0);
10438
+ return eventFailure(
10439
+ -bootstrapIndex - 1,
10440
+ event,
10441
+ "write",
10442
+ "bootstrap buffer mapping is unavailable on the replay backend"
10443
+ );
10444
+ }
10445
+ try {
10446
+ const range = mapped.getMappedRange();
10447
+ if (!range.ok) return eventFailure(-bootstrapIndex - 1, event, "write", range.error);
10448
+ const target = new Uint8Array(range.value);
10449
+ for (const slice of resource.initialData) {
10450
+ const blob2 = context.tape.blobs.find((candidate) => candidate.hash === slice.hash);
10451
+ if (blob2 === void 0) {
10452
+ return eventFailure(-bootstrapIndex - 1, event, "lookup", `blob ${slice.hash} is missing`);
10453
+ }
10454
+ const end = slice.byteOffset + slice.byteLength;
10455
+ if (slice.byteOffset < 0 || slice.byteLength < 0 || end > blob2.bytes.byteLength) {
10456
+ return eventFailure(
10457
+ -bootstrapIndex - 1,
10458
+ event,
10459
+ "lookup",
10460
+ `blob ${slice.hash} does not contain initialData slice [${slice.byteOffset}, ${end})`
10461
+ );
10462
+ }
10463
+ if (slice.byteLength > target.byteLength) {
10464
+ return eventFailure(-bootstrapIndex - 1, event, "write", "initialData exceeds buffer size");
10465
+ }
10466
+ target.set(blob2.bytes.subarray(slice.byteOffset, end));
10149
10467
  }
10468
+ } finally {
10469
+ mapped.unmap();
10150
10470
  }
10151
10471
  return ok(void 0);
10152
10472
  }
@@ -10245,12 +10565,8 @@ async function finalizeWorkPass(context, index, work) {
10245
10565
  `pass ${begin.passHandleId} is not open`
10246
10566
  );
10247
10567
  }
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);
10568
+ const closed = closeReplayPass(context, entry, begin, pass, work.eventIndex);
10569
+ if (!closed.ok) return closed;
10254
10570
  const finishEventIndex = findNextEvent(
10255
10571
  context.tape.events,
10256
10572
  pass.beginEventIndex,
@@ -10266,6 +10582,13 @@ async function finalizeWorkPass(context, index, work) {
10266
10582
  `encoder ${begin.cmdHandleId} is not available`
10267
10583
  );
10268
10584
  }
10585
+ const closure = await replayCommandClosure(
10586
+ context,
10587
+ begin.cmdHandleId,
10588
+ (pass.endEventIndex ?? work.eventIndex) + 1,
10589
+ finishEventIndex ?? context.tape.events.length
10590
+ );
10591
+ if (!closure.ok) return closure;
10269
10592
  const finished = encoder.resource.value.finish();
10270
10593
  const finishEvent = context.tape.events[finishEventIndex ?? work.eventIndex] ?? begin;
10271
10594
  if (!finished.ok)
@@ -10293,6 +10616,95 @@ async function finalizeWorkPass(context, index, work) {
10293
10616
  await context.queue.onSubmittedWorkDone();
10294
10617
  return ok(void 0);
10295
10618
  }
10619
+ function closeReplayPass(context, entry, begin, pass, selectedEventIndex) {
10620
+ const activeQuery = activeOcclusionQuery(
10621
+ context.tape.events,
10622
+ begin.kind === "beginRenderPass" ? begin.passHandleId : void 0,
10623
+ pass.beginEventIndex,
10624
+ selectedEventIndex
10625
+ );
10626
+ if (!activeQuery.ok) return activeQuery;
10627
+ try {
10628
+ if (activeQuery.value !== void 0) {
10629
+ if (entry.resource.role !== "render-pass") {
10630
+ return eventFailure(
10631
+ activeQuery.value.eventIndex,
10632
+ activeQuery.value.event,
10633
+ "lookup",
10634
+ "an active occlusion query belongs to a non-render pass"
10635
+ );
10636
+ }
10637
+ const endedQuery = entry.resource.value.endOcclusionQuery();
10638
+ if (!endedQuery.ok) {
10639
+ return eventFailure(
10640
+ activeQuery.value.eventIndex,
10641
+ activeQuery.value.event,
10642
+ "encode",
10643
+ endedQuery.error
10644
+ );
10645
+ }
10646
+ }
10647
+ entry.resource.value.end();
10648
+ } catch (cause) {
10649
+ return eventFailure(pass.endEventIndex ?? selectedEventIndex, begin, "encode", cause);
10650
+ }
10651
+ context.table.delete(begin.passHandleId);
10652
+ return ok(void 0);
10653
+ }
10654
+ async function replayCommandClosure(context, cmdHandleId, start, end) {
10655
+ for (let eventIndex = start; eventIndex < end; eventIndex++) {
10656
+ const event = context.tape.events[eventIndex];
10657
+ if (event === void 0 || !isCommandClosureEvent(event)) continue;
10658
+ if (event.cmdHandleId !== cmdHandleId) continue;
10659
+ const result = await executeEvent(context, event, eventIndex);
10660
+ if (!result.ok) return result;
10661
+ }
10662
+ return ok(void 0);
10663
+ }
10664
+ function activeOcclusionQuery(events, passHandleId, beginEventIndex, selectedEventIndex) {
10665
+ if (passHandleId === void 0) return ok(void 0);
10666
+ let active;
10667
+ for (let eventIndex = beginEventIndex + 1; eventIndex <= selectedEventIndex; eventIndex++) {
10668
+ const event = events[eventIndex];
10669
+ if (event === void 0 || !("passHandleId" in event) || event.passHandleId !== passHandleId)
10670
+ continue;
10671
+ if (event.kind === "beginOcclusionQuery") {
10672
+ if (active !== void 0) {
10673
+ return eventFailure(
10674
+ eventIndex,
10675
+ event,
10676
+ "encode",
10677
+ "an occlusion query is already active for this render pass"
10678
+ );
10679
+ }
10680
+ active = { eventIndex, event };
10681
+ } else if (event.kind === "endOcclusionQuery") {
10682
+ if (active === void 0) {
10683
+ return eventFailure(
10684
+ eventIndex,
10685
+ event,
10686
+ "encode",
10687
+ "an occlusion query ended without a matching begin"
10688
+ );
10689
+ }
10690
+ active = void 0;
10691
+ }
10692
+ }
10693
+ return ok(active);
10694
+ }
10695
+ function isCommandClosureEvent(event) {
10696
+ switch (event.kind) {
10697
+ case "resolveQuerySet":
10698
+ case "copyBufferToBuffer":
10699
+ case "copyBufferToTexture":
10700
+ case "copyTextureToBuffer":
10701
+ case "copyTextureToTexture":
10702
+ case "clearBuffer":
10703
+ return true;
10704
+ default:
10705
+ return false;
10706
+ }
10707
+ }
10296
10708
  function findNextEvent(events, start, kind, commandId) {
10297
10709
  for (let index = start + 1; index < events.length; index++) {
10298
10710
  const event = events[index];