@forgeax/engine-rhi-debug 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/readback-matrix-fixture.d.ts +32 -0
- package/dist/__tests__/readback-matrix-fixture.d.ts.map +1 -0
- package/dist/frame-model.d.ts +84 -52
- package/dist/frame-model.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +395 -40
- package/dist/index.mjs.map +1 -1
- package/dist/replay/device-request.d.ts +8 -0
- package/dist/replay/device-request.d.ts.map +1 -0
- package/dist/replay/readback.d.ts +7 -0
- package/dist/replay/readback.d.ts.map +1 -1
- package/dist/replay/session.d.ts +17 -0
- package/dist/replay/session.d.ts.map +1 -1
- package/dist/texel-decode.d.ts +5 -4
- package/dist/texel-decode.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/consumer-inventory.unit.test.ts +49 -1
- package/src/__tests__/e2e.browser.test.ts +6 -1
- package/src/__tests__/frame-model-parity.test-d.ts +13 -1
- package/src/__tests__/frame-model-parity.unit.test.ts +262 -15
- package/src/__tests__/guard-gates.test.ts +176 -1
- package/src/__tests__/public-surface.integration.test.ts +8 -0
- package/src/__tests__/readback-format-matrix.dawn.test.ts +226 -3
- package/src/__tests__/readback-format-matrix.unit.test.ts +25 -1
- package/src/__tests__/readback-matrix-fixture.ts +20 -0
- package/src/__tests__/replay-fail-fast.unit.test.ts +12 -0
- package/src/__tests__/replay-session.dawn.test.ts +3 -0
- package/src/__tests__/replay-session.test-d.ts +7 -1
- package/src/__tests__/replay-session.unit.test.ts +16 -0
- package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +2 -0
- package/src/__tests__/tape-index.unit.test.ts +3 -0
- package/src/__tests__/tree-shake.unit.test.ts +24 -0
- package/src/frame-model.ts +381 -83
- package/src/index.ts +17 -10
- package/src/replay/device-request.ts +38 -0
- package/src/replay/readback.ts +106 -18
- package/src/replay/session.ts +47 -2
- package/src/texel-decode.ts +37 -3
package/dist/index.mjs
CHANGED
|
@@ -4718,30 +4718,254 @@ function computeTextureLayout(format, width, height, layerCount, mipLevelCount)
|
|
|
4718
4718
|
}
|
|
4719
4719
|
|
|
4720
4720
|
// src/frame-model.ts
|
|
4721
|
+
function jsonValue(value) {
|
|
4722
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
4723
|
+
return value;
|
|
4724
|
+
if (value === void 0) return null;
|
|
4725
|
+
if (Array.isArray(value)) return value.map(jsonValue);
|
|
4726
|
+
if (ArrayBuffer.isView(value))
|
|
4727
|
+
return Array.from(value, jsonValue);
|
|
4728
|
+
if (typeof value === "object") {
|
|
4729
|
+
const output = {};
|
|
4730
|
+
for (const [key, child] of Object.entries(value)) {
|
|
4731
|
+
if (child !== void 0) output[key] = jsonValue(child);
|
|
4732
|
+
}
|
|
4733
|
+
return output;
|
|
4734
|
+
}
|
|
4735
|
+
return String(value);
|
|
4736
|
+
}
|
|
4737
|
+
function eventDescriptor(event) {
|
|
4738
|
+
return jsonValue(event);
|
|
4739
|
+
}
|
|
4740
|
+
function workPipeline(pipelineId, pipelineEvents, shaderEvents) {
|
|
4741
|
+
if (pipelineId === void 0)
|
|
4742
|
+
return { status: "unavailable", shaders: [], reason: "pipeline-not-bound" };
|
|
4743
|
+
const event = pipelineEvents.get(pipelineId);
|
|
4744
|
+
if (event === void 0)
|
|
4745
|
+
return { status: "unavailable", shaders: [], reason: "descriptor-unavailable" };
|
|
4746
|
+
const shaders = [];
|
|
4747
|
+
const shaderRefs = event.kind === "createRenderPipeline" ? [
|
|
4748
|
+
["vertex", event.vertexShaderModuleHandleId, event.desc.vertex?.entryPoint],
|
|
4749
|
+
["fragment", event.fragmentShaderModuleHandleId, event.desc.fragment?.entryPoint]
|
|
4750
|
+
] : event.kind === "createComputePipeline" ? [["compute", event.computeShaderModuleHandleId, event.desc.compute.entryPoint]] : [];
|
|
4751
|
+
for (const [stage, moduleId, entryPoint] of shaderRefs) {
|
|
4752
|
+
if (typeof moduleId !== "string") continue;
|
|
4753
|
+
const shader = shaderEvents.get(moduleId);
|
|
4754
|
+
shaders.push({
|
|
4755
|
+
stage,
|
|
4756
|
+
moduleHandleId: moduleId,
|
|
4757
|
+
entryPoint: typeof entryPoint === "string" ? entryPoint : null,
|
|
4758
|
+
source: shader?.kind === "createShaderModule" ? shader.wgslCode : null
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
return {
|
|
4762
|
+
status: "available",
|
|
4763
|
+
pipelineHandleId: pipelineId,
|
|
4764
|
+
kind: event.kind === "createRenderPipeline" ? "render" : "compute",
|
|
4765
|
+
descriptor: eventDescriptor(event),
|
|
4766
|
+
shaders
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4721
4769
|
function buildFrameModel(tape) {
|
|
4722
4770
|
const index = buildTapeIndex(tape);
|
|
4723
|
-
const
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4771
|
+
const events = tape.events;
|
|
4772
|
+
const passCommandIndices = index.passes.map(() => []);
|
|
4773
|
+
const commands = [];
|
|
4774
|
+
const groupPath = [];
|
|
4775
|
+
const currentPassByEvent = index.passIndexByEvent;
|
|
4776
|
+
const passAttachmentByIndex = /* @__PURE__ */ new Map();
|
|
4777
|
+
for (const pass of index.passes) {
|
|
4778
|
+
passAttachmentByIndex.set(pass.passIndex, {
|
|
4779
|
+
passIndex: pass.passIndex,
|
|
4780
|
+
kind: pass.kind,
|
|
4781
|
+
beginEventIndex: pass.beginEventIndex,
|
|
4782
|
+
endEventIndex: pass.endEventIndex ?? null,
|
|
4783
|
+
workIndices: pass.workIndices,
|
|
4784
|
+
commandIndices: [],
|
|
4785
|
+
colorAttachmentViewHandleIds: [],
|
|
4786
|
+
depthStencilViewHandleId: null
|
|
4787
|
+
});
|
|
4788
|
+
const begin = events[pass.beginEventIndex];
|
|
4789
|
+
if (begin?.kind === "beginRenderPass") {
|
|
4790
|
+
const attachment = passAttachmentByIndex.get(pass.passIndex);
|
|
4791
|
+
if (attachment !== void 0) {
|
|
4792
|
+
attachment.colorAttachmentViewHandleIds = begin.colorAttachmentViewHandleIds.filter(
|
|
4793
|
+
(id) => typeof id === "string"
|
|
4794
|
+
);
|
|
4795
|
+
attachment.depthStencilViewHandleId = begin.depthStencilViewHandleId ?? null;
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
for (const [eventIndex, event] of events.entries()) {
|
|
4800
|
+
const passIndex = currentPassByEvent[eventIndex] ?? -1;
|
|
4801
|
+
const isPush = event.kind === "pushDebugGroup" || event.kind === "passPushDebugGroup";
|
|
4802
|
+
const isPop = event.kind === "popDebugGroup" || event.kind === "passPopDebugGroup";
|
|
4803
|
+
const group = isPush ? [...groupPath, event.groupLabel] : [...groupPath];
|
|
4804
|
+
const marker = event.kind === "insertDebugMarker" || event.kind === "passInsertDebugMarker" ? event.markerLabel : void 0;
|
|
4805
|
+
const commandIndex = commands.length;
|
|
4806
|
+
commands.push({
|
|
4807
|
+
eventIndex,
|
|
4808
|
+
passIndex,
|
|
4736
4809
|
kind: event.kind,
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4810
|
+
category: EVENT_SEMANTICS[event.kind].category,
|
|
4811
|
+
isWork: isWorkEvent(event.kind),
|
|
4812
|
+
params: eventDescriptor(event),
|
|
4813
|
+
group,
|
|
4814
|
+
...marker === void 0 ? {} : { marker }
|
|
4815
|
+
});
|
|
4816
|
+
if (passIndex >= 0) passCommandIndices[passIndex]?.push(commandIndex);
|
|
4817
|
+
if (isPush) groupPath.push(event.groupLabel);
|
|
4818
|
+
if (isPop) groupPath.pop();
|
|
4819
|
+
}
|
|
4820
|
+
const pipelineEvents = /* @__PURE__ */ new Map();
|
|
4821
|
+
const shaderEvents = /* @__PURE__ */ new Map();
|
|
4822
|
+
const bindGroups = /* @__PURE__ */ new Map();
|
|
4823
|
+
const resourceRecords = /* @__PURE__ */ new Map();
|
|
4824
|
+
const workByEvent = /* @__PURE__ */ new Map();
|
|
4825
|
+
for (const work of index.works) workByEvent.set(work.eventIndex, work.workIndex);
|
|
4826
|
+
for (const bootstrap of tape.bootstrap) {
|
|
4827
|
+
const create = bootstrap.create;
|
|
4828
|
+
if (create.kind === "createRenderPipeline" || create.kind === "createComputePipeline")
|
|
4829
|
+
pipelineEvents.set(create.handleId, create);
|
|
4830
|
+
if (create.kind === "createShaderModule") shaderEvents.set(create.handleId, create);
|
|
4831
|
+
if (create.kind === "createBindGroup") bindGroups.set(create.handleId, create);
|
|
4832
|
+
resourceRecords.set(bootstrap.handleId, {
|
|
4833
|
+
resourceId: bootstrap.handleId,
|
|
4834
|
+
kind: bootstrap.kind,
|
|
4835
|
+
origin: "bootstrap",
|
|
4836
|
+
createEventIndex: null,
|
|
4837
|
+
destroyEventIndex: null,
|
|
4838
|
+
descriptor: jsonValue(bootstrap.create),
|
|
4839
|
+
consumers: [],
|
|
4840
|
+
lifecycle: { state: "unavailable", byteEstimate: null }
|
|
4841
|
+
});
|
|
4842
|
+
}
|
|
4843
|
+
for (const [eventIndex, event] of events.entries()) {
|
|
4844
|
+
if (event.kind === "createRenderPipeline" || event.kind === "createComputePipeline")
|
|
4845
|
+
pipelineEvents.set(event.handleId, event);
|
|
4846
|
+
if (event.kind === "createShaderModule") shaderEvents.set(event.handleId, event);
|
|
4847
|
+
if (event.kind === "createBindGroup") bindGroups.set(event.handleId, event);
|
|
4848
|
+
const kind = resourceKindForEvent(event.kind);
|
|
4849
|
+
const handleId = kind === void 0 ? void 0 : event.kind === "createTextureView" ? event.resultHandleId : event.kind === "createCommandEncoder" ? event.cmdHandleId : "handleId" in event ? event.handleId : void 0;
|
|
4850
|
+
if (kind !== void 0 && handleId !== void 0) {
|
|
4851
|
+
resourceRecords.set(handleId, {
|
|
4852
|
+
resourceId: handleId,
|
|
4853
|
+
kind,
|
|
4854
|
+
origin: "frame",
|
|
4855
|
+
createEventIndex: eventIndex,
|
|
4856
|
+
destroyEventIndex: null,
|
|
4857
|
+
descriptor: eventDescriptor(event),
|
|
4858
|
+
consumers: [],
|
|
4859
|
+
lifecycle: { state: "unavailable", byteEstimate: null }
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
}
|
|
4863
|
+
const lifecycle = buildResourceLifecycle(events);
|
|
4864
|
+
for (const record of lifecycle.resources) {
|
|
4865
|
+
const resource = resourceRecords.get(record.handleId);
|
|
4866
|
+
if (resource === void 0) continue;
|
|
4867
|
+
resourceRecords.set(record.handleId, {
|
|
4868
|
+
...resource,
|
|
4869
|
+
destroyEventIndex: record.destroyedEventIndex ?? null,
|
|
4870
|
+
lifecycle: { state: record.state, byteEstimate: record.byteEstimate }
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4873
|
+
for (const [eventIndex, event] of events.entries()) {
|
|
4874
|
+
const workIndex = workByEvent.get(eventIndex) ?? null;
|
|
4875
|
+
for (const resourceId of EVENT_SEMANTICS[event.kind].read(event)) {
|
|
4876
|
+
const resource = resourceRecords.get(resourceId);
|
|
4877
|
+
if (resource !== void 0)
|
|
4878
|
+
resource.consumers = [...resource.consumers, { eventIndex, workIndex, access: "read" }];
|
|
4879
|
+
}
|
|
4880
|
+
for (const resourceId of EVENT_SEMANTICS[event.kind].written(event)) {
|
|
4881
|
+
const resource = resourceRecords.get(resourceId);
|
|
4882
|
+
if (resource !== void 0)
|
|
4883
|
+
resource.consumers = [...resource.consumers, { eventIndex, workIndex, access: "write" }];
|
|
4884
|
+
}
|
|
4885
|
+
}
|
|
4886
|
+
const works = [];
|
|
4887
|
+
const currentPipeline = /* @__PURE__ */ new Map();
|
|
4888
|
+
const currentVertexBuffers = /* @__PURE__ */ new Map();
|
|
4889
|
+
const currentIndexBuffers = /* @__PURE__ */ new Map();
|
|
4890
|
+
const currentBindGroups = /* @__PURE__ */ new Map();
|
|
4891
|
+
const workByEventEntry = new Map(index.works.map((work) => [work.eventIndex, work]));
|
|
4892
|
+
for (const [eventIndex, event] of events.entries()) {
|
|
4893
|
+
if (event === void 0) continue;
|
|
4894
|
+
const passHandleId = "passHandleId" in event ? event.passHandleId : "";
|
|
4895
|
+
if (event.kind === "setPipeline" || event.kind === "setComputePipeline")
|
|
4896
|
+
currentPipeline.set(passHandleId, event.pipelineHandleId);
|
|
4897
|
+
if (event.kind === "setVertexBuffer") {
|
|
4898
|
+
const buffers = currentVertexBuffers.get(passHandleId) ?? /* @__PURE__ */ new Map();
|
|
4899
|
+
buffers.set(event.slot, {
|
|
4900
|
+
bufferHandleId: event.bufferHandleId,
|
|
4901
|
+
offset: event.offset ?? 0,
|
|
4902
|
+
size: event.size ?? null
|
|
4903
|
+
});
|
|
4904
|
+
currentVertexBuffers.set(passHandleId, buffers);
|
|
4905
|
+
}
|
|
4906
|
+
if (event.kind === "setIndexBuffer")
|
|
4907
|
+
currentIndexBuffers.set(passHandleId, {
|
|
4908
|
+
bufferHandleId: event.bufferHandleId,
|
|
4909
|
+
format: event.format,
|
|
4910
|
+
offset: event.offset ?? 0,
|
|
4911
|
+
size: event.size ?? null
|
|
4912
|
+
});
|
|
4913
|
+
if (event.kind === "setBindGroup") {
|
|
4914
|
+
const groups2 = currentBindGroups.get(passHandleId) ?? /* @__PURE__ */ new Map();
|
|
4915
|
+
groups2.set(event.index, event.bindGroupHandleId);
|
|
4916
|
+
currentBindGroups.set(passHandleId, groups2);
|
|
4917
|
+
}
|
|
4918
|
+
if (!isWorkEvent(event.kind)) continue;
|
|
4919
|
+
const work = workByEventEntry.get(eventIndex);
|
|
4920
|
+
if (work === void 0) continue;
|
|
4921
|
+
const pipelineId = currentPipeline.get(passHandleId);
|
|
4922
|
+
const groups = currentBindGroups.get(passHandleId) ?? /* @__PURE__ */ new Map();
|
|
4923
|
+
const bindings = [];
|
|
4924
|
+
for (const [groupIndex, bindGroupId] of groups) {
|
|
4925
|
+
const bindGroup = bindGroups.get(bindGroupId);
|
|
4926
|
+
for (const [entryIndex, resourceId] of bindGroup?.resourceHandleIds.entries() ?? []) {
|
|
4927
|
+
const entry = bindGroup?.entries[entryIndex];
|
|
4928
|
+
bindings.push({
|
|
4929
|
+
groupIndex,
|
|
4930
|
+
binding: entry?.binding ?? entryIndex,
|
|
4931
|
+
bindGroupId,
|
|
4932
|
+
resourceId: resourceId ?? null,
|
|
4933
|
+
resourceKind: entry?.resourceKind ?? null,
|
|
4934
|
+
bufferOffset: entry?.bufferOffset ?? null,
|
|
4935
|
+
bufferSize: entry?.bufferSize ?? null
|
|
4936
|
+
});
|
|
4937
|
+
}
|
|
4938
|
+
}
|
|
4939
|
+
const attachment = passAttachmentByIndex.get(work.passIndex);
|
|
4940
|
+
works.push({
|
|
4941
|
+
...work,
|
|
4942
|
+
commandIndex: commands.findIndex((command) => command.eventIndex === work.eventIndex),
|
|
4943
|
+
drawCall: eventDescriptor(event),
|
|
4944
|
+
pipeline: workPipeline(pipelineId, pipelineEvents, shaderEvents),
|
|
4945
|
+
bindings,
|
|
4946
|
+
vertexBuffers: [...currentVertexBuffers.get(passHandleId)?.entries() ?? []].map(
|
|
4947
|
+
([slot, buffer]) => ({ slot, ...buffer })
|
|
4948
|
+
),
|
|
4949
|
+
indexBuffer: currentIndexBuffers.get(passHandleId) ?? null,
|
|
4950
|
+
attachments: attachment === void 0 ? null : {
|
|
4951
|
+
colorViewHandleIds: attachment.colorAttachmentViewHandleIds,
|
|
4952
|
+
depthStencilViewHandleId: attachment.depthStencilViewHandleId
|
|
4953
|
+
}
|
|
4954
|
+
});
|
|
4955
|
+
}
|
|
4956
|
+
const passes = index.passes.map((pass) => ({
|
|
4957
|
+
...pass,
|
|
4958
|
+
endEventIndex: pass.endEventIndex ?? null,
|
|
4959
|
+
commandIndices: passCommandIndices[pass.passIndex] ?? [],
|
|
4960
|
+
colorAttachmentViewHandleIds: passAttachmentByIndex.get(pass.passIndex)?.colorAttachmentViewHandleIds ?? [],
|
|
4961
|
+
depthStencilViewHandleId: passAttachmentByIndex.get(pass.passIndex)?.depthStencilViewHandleId ?? null
|
|
4962
|
+
}));
|
|
4963
|
+
return {
|
|
4964
|
+
commands,
|
|
4965
|
+
passes,
|
|
4966
|
+
resources: [...resourceRecords.values()],
|
|
4967
|
+
resourceLifecycle: lifecycle,
|
|
4968
|
+
works
|
|
4745
4969
|
};
|
|
4746
4970
|
}
|
|
4747
4971
|
function asPositiveInteger(value) {
|
|
@@ -8128,6 +8352,31 @@ function attachRecorder(backend, options = {}) {
|
|
|
8128
8352
|
};
|
|
8129
8353
|
return ok(attachment);
|
|
8130
8354
|
}
|
|
8355
|
+
|
|
8356
|
+
// src/replay/device-request.ts
|
|
8357
|
+
var RECORDED_CAPABILITY_FEATURES = [
|
|
8358
|
+
["timestampQuery", "timestamp-query"],
|
|
8359
|
+
["textureCompressionBc", "texture-compression-bc"],
|
|
8360
|
+
["textureCompressionEtc2", "texture-compression-etc2"],
|
|
8361
|
+
["textureCompressionAstc", "texture-compression-astc"],
|
|
8362
|
+
["firstInstanceIndirect", "indirect-first-instance"],
|
|
8363
|
+
["float32Filterable", "float32-filterable"],
|
|
8364
|
+
["rg11b10ufloatRenderable", "rg11b10ufloat-renderable"]
|
|
8365
|
+
];
|
|
8366
|
+
function replayDeviceRequest(tape, adapterFeatures, adapterLimits) {
|
|
8367
|
+
const requiredFeatures = RECORDED_CAPABILITY_FEATURES.filter(
|
|
8368
|
+
([capability, feature]) => tape.header.rhiCaps[capability] === true && adapterFeatures.has(feature)
|
|
8369
|
+
).map(([, feature]) => feature);
|
|
8370
|
+
const limitEntries = Object.entries(adapterLimits).filter(
|
|
8371
|
+
([, value]) => Number.isFinite(value) && value >= 0
|
|
8372
|
+
);
|
|
8373
|
+
const request = {};
|
|
8374
|
+
if (requiredFeatures.length > 0) request.requiredFeatures = requiredFeatures;
|
|
8375
|
+
if (limitEntries.length > 0) {
|
|
8376
|
+
request.requiredLimits = Object.fromEntries(limitEntries);
|
|
8377
|
+
}
|
|
8378
|
+
return request;
|
|
8379
|
+
}
|
|
8131
8380
|
function eventRecord(event) {
|
|
8132
8381
|
return JSON.parse(JSON.stringify(event));
|
|
8133
8382
|
}
|
|
@@ -9059,18 +9308,59 @@ async function readReplayResource(device, table, resourceId, subresource, create
|
|
|
9059
9308
|
const entry = table.get(resourceId);
|
|
9060
9309
|
if (entry === void 0)
|
|
9061
9310
|
return readbackFailure(`resource ${resourceId} is not present in the current generation`);
|
|
9311
|
+
let result;
|
|
9062
9312
|
if (entry.resource.kind === "texture-view") {
|
|
9063
9313
|
const sourceId = stringField2(entry.descriptor, "sourceHandleId");
|
|
9064
9314
|
const source = sourceId === void 0 ? void 0 : table.get(sourceId);
|
|
9065
9315
|
if (source === void 0 || source.resource.kind !== "texture") {
|
|
9066
9316
|
return readbackFailure(`texture view ${resourceId} has no readable source texture`);
|
|
9067
9317
|
}
|
|
9068
|
-
|
|
9318
|
+
const sourceSubresource = resolveTextureViewSubresource(entry, source, subresource);
|
|
9319
|
+
if (!sourceSubresource.ok) return sourceSubresource;
|
|
9320
|
+
result = await readTexture(
|
|
9321
|
+
device,
|
|
9322
|
+
source,
|
|
9323
|
+
resourceId,
|
|
9324
|
+
sourceSubresource.value,
|
|
9325
|
+
createShaderModule2
|
|
9326
|
+
);
|
|
9327
|
+
} else if (entry.resource.kind === "texture") {
|
|
9328
|
+
result = await readTexture(device, entry, resourceId, subresource, createShaderModule2);
|
|
9329
|
+
} else if (entry.resource.kind === "buffer") {
|
|
9330
|
+
result = await readBuffer(device, entry, resourceId, subresource);
|
|
9331
|
+
} else {
|
|
9332
|
+
return readbackFailure(`resource ${resourceId} is not readable by the v7 core matrix`);
|
|
9333
|
+
}
|
|
9334
|
+
if (!result.ok) return result;
|
|
9335
|
+
return ok({
|
|
9336
|
+
...result.value,
|
|
9337
|
+
provenance: {
|
|
9338
|
+
generation: table.generation,
|
|
9339
|
+
resourceId,
|
|
9340
|
+
subresource: subresource ?? null
|
|
9341
|
+
}
|
|
9342
|
+
});
|
|
9343
|
+
}
|
|
9344
|
+
function resolveTextureViewSubresource(view, source, requested) {
|
|
9345
|
+
if (requested === void 0 || isBufferRange(requested)) return ok(requested);
|
|
9346
|
+
const sourceDescriptor = recordField(source.descriptor, "desc");
|
|
9347
|
+
const sourceSize = textureSize(sourceDescriptor?.size);
|
|
9348
|
+
const sourceMipCount = numberField(sourceDescriptor, "mipLevelCount") ?? 1;
|
|
9349
|
+
const viewDescriptor = recordField(view.descriptor, "desc");
|
|
9350
|
+
const baseMipLevel = numberField(viewDescriptor, "baseMipLevel") ?? 0;
|
|
9351
|
+
const baseArrayLayer = numberField(viewDescriptor, "baseArrayLayer") ?? 0;
|
|
9352
|
+
const mipLevelCount = numberField(viewDescriptor, "mipLevelCount") ?? sourceMipCount - baseMipLevel;
|
|
9353
|
+
const arrayLayerCount = numberField(viewDescriptor, "arrayLayerCount") ?? sourceSize.depthOrArrayLayers - baseArrayLayer;
|
|
9354
|
+
const localMipLevel = requested.mipLevel ?? 0;
|
|
9355
|
+
const localArrayLayer = requested.arrayLayer ?? 0;
|
|
9356
|
+
if (!validIndex(baseMipLevel, sourceMipCount + 1) || !validIndex(baseArrayLayer, sourceSize.depthOrArrayLayers + 1) || !validIndex(localMipLevel, mipLevelCount) || !validIndex(localArrayLayer, arrayLayerCount) || baseMipLevel + mipLevelCount > sourceMipCount || baseArrayLayer + arrayLayerCount > sourceSize.depthOrArrayLayers) {
|
|
9357
|
+
return readbackFailure("texture view subresource is outside the recorded view extent");
|
|
9069
9358
|
}
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9359
|
+
return ok({
|
|
9360
|
+
...requested,
|
|
9361
|
+
mipLevel: baseMipLevel + localMipLevel,
|
|
9362
|
+
arrayLayer: baseArrayLayer + localArrayLayer
|
|
9363
|
+
});
|
|
9074
9364
|
}
|
|
9075
9365
|
async function readBuffer(device, entry, resourceId, subresource) {
|
|
9076
9366
|
const size = numberField(recordField(entry.descriptor, "desc"), "size");
|
|
@@ -9118,18 +9408,15 @@ async function readTexture(device, entry, resourceId, subresource, createShaderM
|
|
|
9118
9408
|
const width = Math.max(1, Math.floor(size.width / 2 ** mipLevel));
|
|
9119
9409
|
const height = Math.max(1, Math.floor(size.height / 2 ** mipLevel));
|
|
9120
9410
|
const requestedAspect = textureAspect(subresource);
|
|
9121
|
-
if (format
|
|
9122
|
-
if (
|
|
9411
|
+
if (isDepthStencilTextureFormat(format)) {
|
|
9412
|
+
if (requestedAspect === "all") {
|
|
9123
9413
|
return readbackUnsupported(
|
|
9124
9414
|
resourceId,
|
|
9125
9415
|
format,
|
|
9126
|
-
|
|
9416
|
+
`${format} requires an explicit depth-only or stencil-only aspect`
|
|
9127
9417
|
);
|
|
9128
9418
|
}
|
|
9129
9419
|
if (requestedAspect === "stencil-only") {
|
|
9130
|
-
if (format !== "depth24plus-stencil8") {
|
|
9131
|
-
return readbackUnsupported(resourceId, format, "depth24plus has no stencil aspect");
|
|
9132
|
-
}
|
|
9133
9420
|
const bytes2 = await copyTextureBytes(
|
|
9134
9421
|
device,
|
|
9135
9422
|
entry.resource.value,
|
|
@@ -9147,6 +9434,24 @@ async function readTexture(device, entry, resourceId, subresource, createShaderM
|
|
|
9147
9434
|
if (!bytes2.ok) return bytes2;
|
|
9148
9435
|
return ok({ resourceId, kind: "texture", format, width, height, bytes: bytes2.value });
|
|
9149
9436
|
}
|
|
9437
|
+
if (format === "depth24plus-stencil8") {
|
|
9438
|
+
return blitDepth24PlusTexture(
|
|
9439
|
+
device,
|
|
9440
|
+
entry.resource.value,
|
|
9441
|
+
resourceId,
|
|
9442
|
+
format,
|
|
9443
|
+
width,
|
|
9444
|
+
height,
|
|
9445
|
+
mipLevel,
|
|
9446
|
+
arrayLayer,
|
|
9447
|
+
createShaderModule2
|
|
9448
|
+
);
|
|
9449
|
+
}
|
|
9450
|
+
}
|
|
9451
|
+
if (format === "depth24plus") {
|
|
9452
|
+
if (requestedAspect === "stencil-only") {
|
|
9453
|
+
return readbackUnsupported(resourceId, format, "depth24plus has no stencil aspect");
|
|
9454
|
+
}
|
|
9150
9455
|
return blitDepth24PlusTexture(
|
|
9151
9456
|
device,
|
|
9152
9457
|
entry.resource.value,
|
|
@@ -9354,7 +9659,10 @@ async function copyBufferBytes(device, source, sourceOffset, size) {
|
|
|
9354
9659
|
let staging;
|
|
9355
9660
|
let mapped;
|
|
9356
9661
|
try {
|
|
9357
|
-
const
|
|
9662
|
+
const alignedSourceOffset = sourceOffset - sourceOffset % 4;
|
|
9663
|
+
const leadingBytes = sourceOffset - alignedSourceOffset;
|
|
9664
|
+
const copySize = align4(leadingBytes + size);
|
|
9665
|
+
const created = device.createBuffer({ size: Math.max(4, copySize), usage: COPY_DST_MAP_READ2 });
|
|
9358
9666
|
if (!created.ok)
|
|
9359
9667
|
return readbackFailure(`staging buffer creation failed: ${created.error.code}`);
|
|
9360
9668
|
staging = created.value;
|
|
@@ -9362,7 +9670,7 @@ async function copyBufferBytes(device, source, sourceOffset, size) {
|
|
|
9362
9670
|
if (!encoderResult.ok)
|
|
9363
9671
|
return readbackFailure(`readback encoder creation failed: ${encoderResult.error.code}`);
|
|
9364
9672
|
const encoder = encoderResult.value;
|
|
9365
|
-
encoder.copyBufferToBuffer(source,
|
|
9673
|
+
encoder.copyBufferToBuffer(source, alignedSourceOffset, staging, 0, copySize);
|
|
9366
9674
|
const finished = encoder.finish();
|
|
9367
9675
|
if (!finished.ok)
|
|
9368
9676
|
return readbackFailure(`readback encoder finish failed: ${finished.error.code}`);
|
|
@@ -9372,9 +9680,9 @@ async function copyBufferBytes(device, source, sourceOffset, size) {
|
|
|
9372
9680
|
const mappedResult = await staging.mapAsync(GPU_MAP_MODE_READ);
|
|
9373
9681
|
if (!mappedResult.ok) return readbackFailure(`readback map failed: ${mappedResult.error.code}`);
|
|
9374
9682
|
mapped = mappedResult.value;
|
|
9375
|
-
const range = mapped.getMappedRange(0,
|
|
9683
|
+
const range = mapped.getMappedRange(0, copySize);
|
|
9376
9684
|
if (!range.ok) return readbackFailure(`readback mapped range failed: ${range.error.code}`);
|
|
9377
|
-
return ok(new Uint8Array(range.value).slice());
|
|
9685
|
+
return ok(new Uint8Array(range.value).slice(leadingBytes, leadingBytes + size));
|
|
9378
9686
|
} catch (cause) {
|
|
9379
9687
|
return readbackFailure(`buffer readback failed: ${messageOf(cause)}`);
|
|
9380
9688
|
} finally {
|
|
@@ -9489,6 +9797,9 @@ function validIndex(value, count) {
|
|
|
9489
9797
|
function align256(value) {
|
|
9490
9798
|
return Math.max(256, Math.ceil(value / 256) * 256);
|
|
9491
9799
|
}
|
|
9800
|
+
function align4(value) {
|
|
9801
|
+
return Math.ceil(value / 4) * 4;
|
|
9802
|
+
}
|
|
9492
9803
|
function messageOf(cause) {
|
|
9493
9804
|
return cause instanceof Error ? cause.message : String(cause);
|
|
9494
9805
|
}
|
|
@@ -9584,6 +9895,7 @@ async function openReplay(tape, backend) {
|
|
|
9584
9895
|
const capabilityFailure = checkCapabilities(tape, backend.device);
|
|
9585
9896
|
if (capabilityFailure !== void 0) return err(capabilityFailure);
|
|
9586
9897
|
const index = buildTapeIndex(tape);
|
|
9898
|
+
const model = buildFrameModel(tape);
|
|
9587
9899
|
const table = new ResourceTable(backend.device, 0);
|
|
9588
9900
|
let disposed = false;
|
|
9589
9901
|
let prepared = false;
|
|
@@ -9621,7 +9933,9 @@ async function openReplay(tape, backend) {
|
|
|
9621
9933
|
if (disposed) return positionError(workIndex, index.works.length);
|
|
9622
9934
|
if (signal?.aborted) return positionError(workIndex, index.works.length);
|
|
9623
9935
|
const work = index.works[workIndex];
|
|
9936
|
+
const modelWork = model.works[workIndex];
|
|
9624
9937
|
if (work === void 0) return positionError(workIndex, index.works.length);
|
|
9938
|
+
if (modelWork === void 0) return positionError(workIndex, index.works.length);
|
|
9625
9939
|
const cleared = await reset();
|
|
9626
9940
|
if (!cleared.ok) return cleared;
|
|
9627
9941
|
const bootstrapped = await prepare();
|
|
@@ -9630,11 +9944,29 @@ async function openReplay(tape, backend) {
|
|
|
9630
9944
|
if (!replayed.ok) return replayed;
|
|
9631
9945
|
const attachment = fields?.includes("pixels") ? await readWorkAttachment(context, index, work) : void 0;
|
|
9632
9946
|
if (attachment !== void 0 && !attachment.ok) return attachment;
|
|
9633
|
-
|
|
9947
|
+
const selectedAttachment = attachment?.ok === true ? {
|
|
9948
|
+
...attachment.value,
|
|
9949
|
+
provenance: {
|
|
9950
|
+
...attachment.value.provenance,
|
|
9951
|
+
selectedWorkIndex: work.workIndex
|
|
9952
|
+
}
|
|
9953
|
+
} : void 0;
|
|
9954
|
+
const baseInspection = {
|
|
9634
9955
|
workIndex: work.workIndex,
|
|
9635
9956
|
eventIndex: work.eventIndex,
|
|
9636
9957
|
passIndex: work.passIndex,
|
|
9637
|
-
attachment:
|
|
9958
|
+
attachment: selectedAttachment
|
|
9959
|
+
};
|
|
9960
|
+
return ok({
|
|
9961
|
+
...baseInspection,
|
|
9962
|
+
...fields?.includes("pipeline") ? { pipeline: modelWork.pipeline } : {},
|
|
9963
|
+
...fields?.includes("bindings") ? {
|
|
9964
|
+
bindings: modelWork.bindings,
|
|
9965
|
+
vertexBuffers: modelWork.vertexBuffers,
|
|
9966
|
+
indexBuffer: modelWork.indexBuffer,
|
|
9967
|
+
shaders: modelWork.pipeline.shaders,
|
|
9968
|
+
resourceIds: modelWork.bindings.map((binding) => binding.resourceId).filter((resourceId) => resourceId !== null)
|
|
9969
|
+
} : {}
|
|
9638
9970
|
});
|
|
9639
9971
|
},
|
|
9640
9972
|
async readResource(resourceId, subresource, signal) {
|
|
@@ -10000,7 +10332,10 @@ function readTexel(view, off, info, channelBytes) {
|
|
|
10000
10332
|
}
|
|
10001
10333
|
return out;
|
|
10002
10334
|
}
|
|
10003
|
-
function decodeToRgba8(bytes, format, width, height) {
|
|
10335
|
+
function decodeToRgba8(bytes, format, width, height, aspect = "all") {
|
|
10336
|
+
if (format.startsWith("depth")) {
|
|
10337
|
+
return decodeDepthToRgba8(bytes, format, width, height, aspect);
|
|
10338
|
+
}
|
|
10004
10339
|
const info = formatInfo(format);
|
|
10005
10340
|
const texBytes = bytesPerTexel(format);
|
|
10006
10341
|
if (!info || texBytes === void 0) return null;
|
|
@@ -10032,6 +10367,26 @@ function decodeToRgba8(bytes, format, width, height) {
|
|
|
10032
10367
|
}
|
|
10033
10368
|
return out;
|
|
10034
10369
|
}
|
|
10370
|
+
function decodeDepthToRgba8(bytes, format, width, height, aspect) {
|
|
10371
|
+
const combined = format.endsWith("-stencil8");
|
|
10372
|
+
if (combined && aspect === "all") return null;
|
|
10373
|
+
if (!combined && aspect === "stencil-only") return null;
|
|
10374
|
+
const stencil = aspect === "stencil-only";
|
|
10375
|
+
const bytesPerValue = stencil ? 1 : 4;
|
|
10376
|
+
const texelCount = width * height;
|
|
10377
|
+
if (bytes.byteLength < texelCount * bytesPerValue) return null;
|
|
10378
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
10379
|
+
const out = new Uint8ClampedArray(new ArrayBuffer(texelCount * 4));
|
|
10380
|
+
for (let index = 0; index < texelCount; index++) {
|
|
10381
|
+
const value = stencil ? view.getUint8(index) : Math.round(clamp01(view.getFloat32(index * 4, true)) * 255);
|
|
10382
|
+
const target = index * 4;
|
|
10383
|
+
out[target] = value;
|
|
10384
|
+
out[target + 1] = value;
|
|
10385
|
+
out[target + 2] = value;
|
|
10386
|
+
out[target + 3] = 255;
|
|
10387
|
+
}
|
|
10388
|
+
return out;
|
|
10389
|
+
}
|
|
10035
10390
|
function decodeTexelRaw(bytes, format, width, height, texelX, texelY) {
|
|
10036
10391
|
if (texelX < 0 || texelX >= width || texelY < 0 || texelY >= height) return null;
|
|
10037
10392
|
const info = formatInfo(format);
|
|
@@ -10055,6 +10410,6 @@ function decodeTexelRaw(bytes, format, width, height, texelX, texelY) {
|
|
|
10055
10410
|
return [r, channels[1], b, a];
|
|
10056
10411
|
}
|
|
10057
10412
|
|
|
10058
|
-
export { EVENT_SEMANTICS, TAPE_MAGIC, TAPE_FORMAT_VERSION as V7_TAPE_FORMAT_VERSION, attachRecorder, buildFrameModel, buildTapeIndex, bytesPerTexel, createRhiDebugError, decodeTape, decodeTexelRaw, decodeToRgba8, encodeTape, eventKinds, formatInfo, halfToFloat, isWorkEvent, openReplay, readbackTexturePixels, resourceKindForEvent, workEventKinds };
|
|
10413
|
+
export { EVENT_SEMANTICS, TAPE_MAGIC, TAPE_FORMAT_VERSION as V7_TAPE_FORMAT_VERSION, attachRecorder, buildFrameModel, buildResourceLifecycle, buildTapeIndex, bytesPerTexel, createRhiDebugError, decodeTape, decodeTexelRaw, decodeToRgba8, encodeTape, eventKinds, formatInfo, halfToFloat, isWorkEvent, openReplay, readbackTexturePixels, replayDeviceRequest, resourceKindForEvent, workEventKinds };
|
|
10059
10414
|
//# sourceMappingURL=index.mjs.map
|
|
10060
10415
|
//# sourceMappingURL=index.mjs.map
|