@forgeax/engine-rhi-debug 0.0.0-dev.8d955ade1c79
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/LICENSE +202 -0
- package/README.md +178 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/bootstrap-integration.test.d.ts +2 -0
- package/dist/__tests__/bootstrap-integration.test.d.ts.map +1 -0
- package/dist/__tests__/consumer-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/consumer-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/copy-src-usage-validity.dawn.test.d.ts +15 -0
- package/dist/__tests__/copy-src-usage-validity.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/coverage-invariant.test-d.d.ts +2 -0
- package/dist/__tests__/coverage-invariant.test-d.d.ts.map +1 -0
- package/dist/__tests__/coverage-invariant.unit.test.d.ts +2 -0
- package/dist/__tests__/coverage-invariant.unit.test.d.ts.map +1 -0
- package/dist/__tests__/e2e.browser.test.d.ts +2 -0
- package/dist/__tests__/e2e.browser.test.d.ts.map +1 -0
- package/dist/__tests__/error-code-narrowing.test-d.d.ts +2 -0
- package/dist/__tests__/error-code-narrowing.test-d.d.ts.map +1 -0
- package/dist/__tests__/errors-v7.test-d.d.ts +2 -0
- package/dist/__tests__/errors-v7.test-d.d.ts.map +1 -0
- package/dist/__tests__/errors-v7.unit.test.d.ts +2 -0
- package/dist/__tests__/errors-v7.unit.test.d.ts.map +1 -0
- package/dist/__tests__/errors.unit.test.d.ts +2 -0
- package/dist/__tests__/errors.unit.test.d.ts.map +1 -0
- package/dist/__tests__/event-semantics-coverage.unit.test.d.ts +2 -0
- package/dist/__tests__/event-semantics-coverage.unit.test.d.ts.map +1 -0
- package/dist/__tests__/format-info.unit.test.d.ts +2 -0
- package/dist/__tests__/format-info.unit.test.d.ts.map +1 -0
- package/dist/__tests__/frame-model-parity.test-d.d.ts +2 -0
- package/dist/__tests__/frame-model-parity.test-d.d.ts.map +1 -0
- package/dist/__tests__/frame-model-parity.unit.test.d.ts +2 -0
- package/dist/__tests__/frame-model-parity.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guard-gates.test.d.ts +2 -0
- package/dist/__tests__/guard-gates.test.d.ts.map +1 -0
- package/dist/__tests__/public-surface.integration.test.d.ts +2 -0
- package/dist/__tests__/public-surface.integration.test.d.ts.map +1 -0
- package/dist/__tests__/readback-fault-cleanup.unit.test.d.ts +2 -0
- package/dist/__tests__/readback-fault-cleanup.unit.test.d.ts.map +1 -0
- package/dist/__tests__/readback-format-matrix.dawn.test.d.ts +2 -0
- package/dist/__tests__/readback-format-matrix.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/readback-format-matrix.unit.test.d.ts +2 -0
- package/dist/__tests__/readback-format-matrix.unit.test.d.ts.map +1 -0
- package/dist/__tests__/readback-matrix-fixture.d.ts +32 -0
- package/dist/__tests__/readback-matrix-fixture.d.ts.map +1 -0
- package/dist/__tests__/recorder-bootstrap.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-bootstrap.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-budget.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-budget.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-fail-fast.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-fail-fast.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-push-event.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-push-event.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-session.integration.test.d.ts +2 -0
- package/dist/__tests__/recorder-session.integration.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-session.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-session.unit.test.d.ts.map +1 -0
- package/dist/__tests__/recorder-steady-frame.dawn.test.d.ts +2 -0
- package/dist/__tests__/recorder-steady-frame.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/replay-fail-fast.unit.test.d.ts +2 -0
- package/dist/__tests__/replay-fail-fast.unit.test.d.ts.map +1 -0
- package/dist/__tests__/replay-session.dawn.test.d.ts +2 -0
- package/dist/__tests__/replay-session.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/replay-session.test-d.d.ts +2 -0
- package/dist/__tests__/replay-session.test-d.d.ts.map +1 -0
- package/dist/__tests__/replay-session.unit.test.d.ts +2 -0
- package/dist/__tests__/replay-session.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-texture-descriptor.unit.test.d.ts +2 -0
- package/dist/__tests__/resolve-texture-descriptor.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resource-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/resource-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/rhi-debug-fresh-replay.dawn.test.d.ts +2 -0
- package/dist/__tests__/rhi-debug-fresh-replay.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/tape-index.unit.test.d.ts +2 -0
- package/dist/__tests__/tape-index.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tape-v7-invalid.unit.test.d.ts +2 -0
- package/dist/__tests__/tape-v7-invalid.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tape-v7-roundtrip.unit.test.d.ts +2 -0
- package/dist/__tests__/tape-v7-roundtrip.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tape-v7-version.unit.test.d.ts +2 -0
- package/dist/__tests__/tape-v7-version.unit.test.d.ts.map +1 -0
- package/dist/__tests__/tree-shake.unit.test.d.ts +2 -0
- package/dist/__tests__/tree-shake.unit.test.d.ts.map +1 -0
- package/dist/__tests__/types.test.d.ts +2 -0
- package/dist/__tests__/types.test.d.ts.map +1 -0
- package/dist/browser.d.ts +45 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.mjs +83 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/errors.d.ts +85 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/frame-model.d.ts +158 -0
- package/dist/frame-model.d.ts.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +10415 -0
- package/dist/index.mjs.map +1 -0
- package/dist/protocol/codec.d.ts +6 -0
- package/dist/protocol/codec.d.ts.map +1 -0
- package/dist/protocol/event-semantics.d.ts +15 -0
- package/dist/protocol/event-semantics.d.ts.map +1 -0
- package/dist/protocol/tape-index.d.ts +30 -0
- package/dist/protocol/tape-index.d.ts.map +1 -0
- package/dist/protocol/types.d.ts +44 -0
- package/dist/protocol/types.d.ts.map +1 -0
- package/dist/protocol/validation.d.ts +7 -0
- package/dist/protocol/validation.d.ts.map +1 -0
- package/dist/readback.d.ts +156 -0
- package/dist/readback.d.ts.map +1 -0
- package/dist/recorder/assemble.d.ts +11 -0
- package/dist/recorder/assemble.d.ts.map +1 -0
- package/dist/recorder/closure.d.ts +39 -0
- package/dist/recorder/closure.d.ts.map +1 -0
- package/dist/recorder/core.d.ts +296 -0
- package/dist/recorder/core.d.ts.map +1 -0
- package/dist/recorder/device.d.ts +4 -0
- package/dist/recorder/device.d.ts.map +1 -0
- package/dist/recorder/encoder.d.ts +5 -0
- package/dist/recorder/encoder.d.ts.map +1 -0
- package/dist/recorder/lifecycle.d.ts +20 -0
- package/dist/recorder/lifecycle.d.ts.map +1 -0
- package/dist/recorder/pass.d.ts +6 -0
- package/dist/recorder/pass.d.ts.map +1 -0
- package/dist/recorder/proxy.d.ts +19 -0
- package/dist/recorder/proxy.d.ts.map +1 -0
- package/dist/recorder/queue.d.ts +4 -0
- package/dist/recorder/queue.d.ts.map +1 -0
- package/dist/recorder/resource-registry.d.ts +15 -0
- package/dist/recorder/resource-registry.d.ts.map +1 -0
- package/dist/recorder/session.d.ts +25 -0
- package/dist/recorder/session.d.ts.map +1 -0
- package/dist/recorder/shader.d.ts +3 -0
- package/dist/recorder/shader.d.ts.map +1 -0
- package/dist/recorder/snapshot.d.ts +10 -0
- package/dist/recorder/snapshot.d.ts.map +1 -0
- package/dist/recorder/wrap.d.ts +4 -0
- package/dist/recorder/wrap.d.ts.map +1 -0
- package/dist/recorder.d.ts +7 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/replay/device-request.d.ts +8 -0
- package/dist/replay/device-request.d.ts.map +1 -0
- package/dist/replay/execute-support.d.ts +33 -0
- package/dist/replay/execute-support.d.ts.map +1 -0
- package/dist/replay/execute.d.ts +18 -0
- package/dist/replay/execute.d.ts.map +1 -0
- package/dist/replay/readback.d.ts +32 -0
- package/dist/replay/readback.d.ts.map +1 -0
- package/dist/replay/resources.d.ts +57 -0
- package/dist/replay/resources.d.ts.map +1 -0
- package/dist/replay/session.d.ts +49 -0
- package/dist/replay/session.d.ts.map +1 -0
- package/dist/replay/texture-format.d.ts +21 -0
- package/dist/replay/texture-format.d.ts.map +1 -0
- package/dist/texel-decode.d.ts +35 -0
- package/dist/texel-decode.d.ts.map +1 -0
- package/dist/texel-layout.d.ts +76 -0
- package/dist/texel-layout.d.ts.map +1 -0
- package/dist/types.d.ts +722 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +72 -0
- package/src/__tests__/bootstrap-integration.test.ts +268 -0
- package/src/__tests__/consumer-inventory.unit.test.ts +154 -0
- package/src/__tests__/copy-src-usage-validity.dawn.test.ts +117 -0
- package/src/__tests__/coverage-invariant.test-d.ts +115 -0
- package/src/__tests__/coverage-invariant.unit.test.ts +45 -0
- package/src/__tests__/e2e.browser.test.ts +239 -0
- package/src/__tests__/error-code-narrowing.test-d.ts +32 -0
- package/src/__tests__/errors-v7.test-d.ts +19 -0
- package/src/__tests__/errors-v7.unit.test.ts +15 -0
- package/src/__tests__/errors.unit.test.ts +83 -0
- package/src/__tests__/event-semantics-coverage.unit.test.ts +22 -0
- package/src/__tests__/format-info.unit.test.ts +85 -0
- package/src/__tests__/frame-model-parity.test-d.ts +20 -0
- package/src/__tests__/frame-model-parity.unit.test.ts +285 -0
- package/src/__tests__/guard-gates.test.ts +317 -0
- package/src/__tests__/public-surface.integration.test.ts +38 -0
- package/src/__tests__/readback-fault-cleanup.unit.test.ts +66 -0
- package/src/__tests__/readback-format-matrix.dawn.test.ts +378 -0
- package/src/__tests__/readback-format-matrix.unit.test.ts +120 -0
- package/src/__tests__/readback-matrix-fixture.ts +20 -0
- package/src/__tests__/recorder-bootstrap.unit.test.ts +302 -0
- package/src/__tests__/recorder-budget.unit.test.ts +42 -0
- package/src/__tests__/recorder-fail-fast.unit.test.ts +434 -0
- package/src/__tests__/recorder-lifecycle.unit.test.ts +42 -0
- package/src/__tests__/recorder-push-event.unit.test.ts +573 -0
- package/src/__tests__/recorder-session.integration.test.ts +32 -0
- package/src/__tests__/recorder-session.unit.test.ts +63 -0
- package/src/__tests__/recorder-steady-frame.dawn.test.ts +52 -0
- package/src/__tests__/replay-fail-fast.unit.test.ts +155 -0
- package/src/__tests__/replay-session.dawn.test.ts +220 -0
- package/src/__tests__/replay-session.test-d.ts +27 -0
- package/src/__tests__/replay-session.unit.test.ts +171 -0
- package/src/__tests__/resolve-texture-descriptor.unit.test.ts +144 -0
- package/src/__tests__/resource-lifecycle.unit.test.ts +114 -0
- package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +178 -0
- package/src/__tests__/tape-index.unit.test.ts +56 -0
- package/src/__tests__/tape-v7-invalid.unit.test.ts +45 -0
- package/src/__tests__/tape-v7-roundtrip.unit.test.ts +66 -0
- package/src/__tests__/tape-v7-version.unit.test.ts +13 -0
- package/src/__tests__/tree-shake.unit.test.ts +151 -0
- package/src/__tests__/types.test.ts +257 -0
- package/src/browser.ts +146 -0
- package/src/errors.ts +173 -0
- package/src/frame-model.ts +753 -0
- package/src/index.ts +92 -0
- package/src/pako.d.ts +8 -0
- package/src/protocol/codec.ts +220 -0
- package/src/protocol/event-semantics.ts +180 -0
- package/src/protocol/tape-index.ts +120 -0
- package/src/protocol/types.ts +60 -0
- package/src/protocol/validation.ts +173 -0
- package/src/readback.ts +804 -0
- package/src/recorder/assemble.ts +192 -0
- package/src/recorder/closure.ts +408 -0
- package/src/recorder/core.ts +587 -0
- package/src/recorder/device.ts +474 -0
- package/src/recorder/encoder.ts +251 -0
- package/src/recorder/lifecycle.ts +847 -0
- package/src/recorder/pass.ts +241 -0
- package/src/recorder/proxy.ts +59 -0
- package/src/recorder/queue.ts +109 -0
- package/src/recorder/resource-registry.ts +53 -0
- package/src/recorder/session.ts +152 -0
- package/src/recorder/shader.ts +47 -0
- package/src/recorder/snapshot.ts +38 -0
- package/src/recorder/wrap.ts +157 -0
- package/src/recorder.ts +15 -0
- package/src/replay/device-request.ts +38 -0
- package/src/replay/execute-support.ts +243 -0
- package/src/replay/execute.ts +870 -0
- package/src/replay/readback.ts +671 -0
- package/src/replay/resources.ts +154 -0
- package/src/replay/session.ts +509 -0
- package/src/replay/texture-format.ts +99 -0
- package/src/texel-decode.ts +289 -0
- package/src/texel-layout.ts +324 -0
- package/src/types.ts +888 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
// @forgeax/engine-rhi-debug/src/__tests__/recorder-push-event.unit.test.ts
|
|
2
|
+
//
|
|
3
|
+
// M3 w8: RED-phase unit tests for recorder proxy pushEvent completeness.
|
|
4
|
+
// Verifies that all 16 commands (7 silent pass-through + 3 encoder-level
|
|
5
|
+
// debug group + 6 new events) produce correct event kinds in the tape.
|
|
6
|
+
// Currently RED because proxy methods in recorder.ts do not pushEvent for
|
|
7
|
+
// these 16 commands (w10 adds them).
|
|
8
|
+
//
|
|
9
|
+
// AC-03: 7 silent pass-through commands each emit correct event kind.
|
|
10
|
+
// AC-04: 3 encoder-level debug group commands each emit correct event kind.
|
|
11
|
+
// AC-05: 6 new events each emit correct event kind; indirect events assert
|
|
12
|
+
// indirectBufferHandleId is present in event payload.
|
|
13
|
+
//
|
|
14
|
+
// Related: requirements AC-03/AC-04/AC-05; plan-strategy 5.1 TDD;
|
|
15
|
+
// research Finding 1/2/3.
|
|
16
|
+
|
|
17
|
+
// biome-ignore-all lint/suspicious/noExplicitAny: test stubs for recorder proxy exercise need mock RHI types at the boundary; RhiCallEvent/RhiRenderPassEncoder/RhiCommandEncoder are structural shapes that vi.fn() stubs cannot type-narrow without any
|
|
18
|
+
// biome-ignore-all lint/style/noNonNullAssertion: test assertions on tape event arrays are guarded by expect.length checks before indexed access
|
|
19
|
+
|
|
20
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
21
|
+
import { type DebugRhiInstance, wrap } from '../recorder';
|
|
22
|
+
import type {
|
|
23
|
+
RhiCallEvent,
|
|
24
|
+
RhiCallEventClearBuffer,
|
|
25
|
+
RhiCallEventCopyBufferToBuffer,
|
|
26
|
+
RhiCallEventDrawIndexedIndirect,
|
|
27
|
+
RhiCallEventDrawIndirect,
|
|
28
|
+
RhiCallEventInsertDebugMarker,
|
|
29
|
+
RhiCallEventPassInsertDebugMarker,
|
|
30
|
+
RhiCallEventPassPushDebugGroup,
|
|
31
|
+
RhiCallEventPushDebugGroup,
|
|
32
|
+
RhiCallEventSetBlendConstant,
|
|
33
|
+
RhiCallEventSetScissorRect,
|
|
34
|
+
RhiCallEventSetViewport,
|
|
35
|
+
} from '../types';
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Helpers
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
function rOk<T>(value: T) {
|
|
42
|
+
return { ok: true as const, value };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build a mock RhiInstance with all RHI encoder/pass methods stubbed so the
|
|
47
|
+
* recorder proxy can exercise the full encoder + render pass surface without
|
|
48
|
+
* a real GPU device. Uses any casts at the RhiInstance boundary since mock
|
|
49
|
+
* Result objects lack the prototype-level unwrap/unwrapOr methods.
|
|
50
|
+
*/
|
|
51
|
+
async function buildMockAndArm(): Promise<{
|
|
52
|
+
debugInst: DebugRhiInstance;
|
|
53
|
+
proxyDevice: any;
|
|
54
|
+
}> {
|
|
55
|
+
function makeMockRenderPass(): any {
|
|
56
|
+
return {
|
|
57
|
+
setPipeline: vi.fn(),
|
|
58
|
+
setVertexBuffer: vi.fn(),
|
|
59
|
+
setIndexBuffer: vi.fn(),
|
|
60
|
+
setBindGroup: vi.fn(),
|
|
61
|
+
draw: vi.fn(),
|
|
62
|
+
drawIndexed: vi.fn(),
|
|
63
|
+
setViewport: vi.fn(),
|
|
64
|
+
setScissorRect: vi.fn(),
|
|
65
|
+
setBlendConstant: vi.fn(),
|
|
66
|
+
setStencilReference: vi.fn(),
|
|
67
|
+
drawIndirect: vi.fn(),
|
|
68
|
+
drawIndexedIndirect: vi.fn(),
|
|
69
|
+
pushDebugGroup: vi.fn(),
|
|
70
|
+
popDebugGroup: vi.fn(),
|
|
71
|
+
insertDebugMarker: vi.fn(),
|
|
72
|
+
end: vi.fn(),
|
|
73
|
+
beginOcclusionQuery: vi.fn(),
|
|
74
|
+
endOcclusionQuery: vi.fn(),
|
|
75
|
+
executeBundles: vi.fn(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeMockComputePass(): any {
|
|
80
|
+
return {
|
|
81
|
+
setPipeline: vi.fn(),
|
|
82
|
+
setBindGroup: vi.fn(),
|
|
83
|
+
dispatchWorkgroups: vi.fn(),
|
|
84
|
+
end: vi.fn(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const mockCmdEncoder: any = {
|
|
89
|
+
beginRenderPass: vi.fn((_desc: any) => makeMockRenderPass()),
|
|
90
|
+
beginComputePass: vi.fn((_desc?: any) => makeMockComputePass()),
|
|
91
|
+
copyBufferToBuffer: vi.fn(),
|
|
92
|
+
copyBufferToTexture: vi.fn(),
|
|
93
|
+
copyTextureToBuffer: vi.fn(),
|
|
94
|
+
copyTextureToTexture: vi.fn(),
|
|
95
|
+
clearBuffer: vi.fn(),
|
|
96
|
+
pushDebugGroup: vi.fn(),
|
|
97
|
+
popDebugGroup: vi.fn(),
|
|
98
|
+
insertDebugMarker: vi.fn(),
|
|
99
|
+
finish: vi.fn(() => rOk({})),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const mockDevice: any = {
|
|
103
|
+
caps: { rgba16floatRenderable: false },
|
|
104
|
+
features: new Set(),
|
|
105
|
+
limits: {},
|
|
106
|
+
queue: {
|
|
107
|
+
writeBuffer: vi.fn(() => rOk(undefined)),
|
|
108
|
+
writeTexture: vi.fn(() => rOk(undefined)),
|
|
109
|
+
submit: vi.fn(() => rOk(undefined)),
|
|
110
|
+
copyExternalImageToTexture: vi.fn(() => rOk(undefined)),
|
|
111
|
+
onSubmittedWorkDone: vi.fn(() => Promise.resolve(undefined)),
|
|
112
|
+
},
|
|
113
|
+
lost: new Promise(() => {}),
|
|
114
|
+
createBuffer: vi.fn(() => rOk({})),
|
|
115
|
+
createTexture: vi.fn(() => rOk({})),
|
|
116
|
+
createTextureView: vi.fn(() => rOk({})),
|
|
117
|
+
createSampler: vi.fn(() => rOk({})),
|
|
118
|
+
createBindGroupLayout: vi.fn(() => rOk({})),
|
|
119
|
+
createBindGroup: vi.fn(() => rOk({})),
|
|
120
|
+
createPipelineLayout: vi.fn(() => rOk({})),
|
|
121
|
+
createRenderPipeline: vi.fn(() => rOk({})),
|
|
122
|
+
createComputePipeline: vi.fn(() => rOk({})),
|
|
123
|
+
createShaderModule: vi.fn(() => rOk({})),
|
|
124
|
+
createCommandEncoder: vi.fn(() => rOk(mockCmdEncoder)),
|
|
125
|
+
createQuerySet: vi.fn(() => rOk({})),
|
|
126
|
+
destroyBuffer: vi.fn(),
|
|
127
|
+
destroyTexture: vi.fn(),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const mockAdapter: any = {
|
|
131
|
+
features: new Set(),
|
|
132
|
+
limits: {},
|
|
133
|
+
requestDevice: vi.fn(() => Promise.resolve(rOk(mockDevice))),
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const mockInst: any = {
|
|
137
|
+
requestAdapter: vi.fn(() => Promise.resolve(rOk(mockAdapter))),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const debugInst = wrap(mockInst);
|
|
141
|
+
const adapterRes = await debugInst.requestAdapter();
|
|
142
|
+
if (!adapterRes.ok) throw new Error('mock adapter failed');
|
|
143
|
+
const adapter = (adapterRes as any).value;
|
|
144
|
+
const devRes = await adapter.requestDevice();
|
|
145
|
+
if (!devRes.ok) throw new Error('mock device failed');
|
|
146
|
+
const proxyDevice = (devRes as any).value;
|
|
147
|
+
|
|
148
|
+
// Arm for 1 frame so pushEvent gate passes
|
|
149
|
+
debugInst.arm(1);
|
|
150
|
+
|
|
151
|
+
return { debugInst, proxyDevice };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Record one frame and return the tape events array.
|
|
156
|
+
*/
|
|
157
|
+
function finalizeAndGetEvents(debugInst: DebugRhiInstance): readonly RhiCallEvent[] {
|
|
158
|
+
debugInst.onFrameEnd();
|
|
159
|
+
const tape = debugInst.getTape() as any;
|
|
160
|
+
if (tape?.events) return tape.events as readonly RhiCallEvent[];
|
|
161
|
+
throw new Error('getTape() returned no events');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function findEventsOfKind(events: readonly RhiCallEvent[], kind: string): RhiCallEvent[] {
|
|
165
|
+
return events.filter((e) => e.kind === kind);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// AC-03: 7 silent pass-through commands
|
|
170
|
+
// ============================================================================
|
|
171
|
+
|
|
172
|
+
describe('w8: AC-03 silent pass-through commands (7 total)', () => {
|
|
173
|
+
it('copyBufferToBuffer pushEvent produces event kind in tape', async () => {
|
|
174
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
175
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
176
|
+
expect(encRes.ok).toBe(true);
|
|
177
|
+
const enc = (encRes as any).value;
|
|
178
|
+
|
|
179
|
+
const bufResA = proxyDevice.createBuffer({ size: 16, usage: 1 });
|
|
180
|
+
const bufResB = proxyDevice.createBuffer({ size: 16, usage: 2 });
|
|
181
|
+
expect(bufResA.ok).toBe(true);
|
|
182
|
+
expect(bufResB.ok).toBe(true);
|
|
183
|
+
const bufA = (bufResA as any).value;
|
|
184
|
+
const bufB = (bufResB as any).value;
|
|
185
|
+
|
|
186
|
+
enc.copyBufferToBuffer(bufA, 0, bufB, 0, 16);
|
|
187
|
+
const finishRes = enc.finish();
|
|
188
|
+
expect(finishRes.ok).toBe(true);
|
|
189
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
190
|
+
|
|
191
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
192
|
+
const copyEvents = findEventsOfKind(events, 'copyBufferToBuffer');
|
|
193
|
+
expect(copyEvents.length).toBe(1);
|
|
194
|
+
const ev = copyEvents[0] as RhiCallEventCopyBufferToBuffer;
|
|
195
|
+
expect(ev.sourceHandleId).toBeDefined();
|
|
196
|
+
expect(ev.destinationHandleId).toBeDefined();
|
|
197
|
+
expect(ev.size).toBe(16);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('copyBufferToTexture pushEvent produces event kind in tape', async () => {
|
|
201
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
202
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
203
|
+
expect(encRes.ok).toBe(true);
|
|
204
|
+
const enc = (encRes as any).value;
|
|
205
|
+
|
|
206
|
+
const bufRes = proxyDevice.createBuffer({ size: 64, usage: 1 });
|
|
207
|
+
const texRes = proxyDevice.createTexture({
|
|
208
|
+
size: { width: 4, height: 4 },
|
|
209
|
+
format: 'rgba8unorm',
|
|
210
|
+
usage: 8,
|
|
211
|
+
});
|
|
212
|
+
expect(bufRes.ok).toBe(true);
|
|
213
|
+
expect(texRes.ok).toBe(true);
|
|
214
|
+
const buf = (bufRes as any).value;
|
|
215
|
+
const tex = (texRes as any).value;
|
|
216
|
+
|
|
217
|
+
enc.copyBufferToTexture(
|
|
218
|
+
{ buffer: buf, bytesPerRow: 16, rowsPerImage: 4 },
|
|
219
|
+
{ texture: tex, mipLevel: 0 },
|
|
220
|
+
{ width: 4, height: 4 },
|
|
221
|
+
);
|
|
222
|
+
const finishRes = enc.finish();
|
|
223
|
+
expect(finishRes.ok).toBe(true);
|
|
224
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
225
|
+
|
|
226
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
227
|
+
const copyEvents = findEventsOfKind(events, 'copyBufferToTexture');
|
|
228
|
+
expect(copyEvents.length).toBe(1);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('copyTextureToBuffer pushEvent produces event kind in tape', async () => {
|
|
232
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
233
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
234
|
+
expect(encRes.ok).toBe(true);
|
|
235
|
+
const enc = (encRes as any).value;
|
|
236
|
+
|
|
237
|
+
const texRes = proxyDevice.createTexture({
|
|
238
|
+
size: { width: 4, height: 4 },
|
|
239
|
+
format: 'rgba8unorm',
|
|
240
|
+
usage: 9,
|
|
241
|
+
});
|
|
242
|
+
const bufRes = proxyDevice.createBuffer({ size: 64, usage: 2 });
|
|
243
|
+
expect(texRes.ok).toBe(true);
|
|
244
|
+
expect(bufRes.ok).toBe(true);
|
|
245
|
+
const tex = (texRes as any).value;
|
|
246
|
+
const buf = (bufRes as any).value;
|
|
247
|
+
|
|
248
|
+
enc.copyTextureToBuffer(
|
|
249
|
+
{ texture: tex, mipLevel: 0 },
|
|
250
|
+
{ buffer: buf, bytesPerRow: 16, rowsPerImage: 4 },
|
|
251
|
+
{ width: 4, height: 4 },
|
|
252
|
+
);
|
|
253
|
+
const finishRes = enc.finish();
|
|
254
|
+
expect(finishRes.ok).toBe(true);
|
|
255
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
256
|
+
|
|
257
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
258
|
+
const copyEvents = findEventsOfKind(events, 'copyTextureToBuffer');
|
|
259
|
+
expect(copyEvents.length).toBe(1);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('copyTextureToTexture pushEvent produces event kind in tape', async () => {
|
|
263
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
264
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
265
|
+
expect(encRes.ok).toBe(true);
|
|
266
|
+
const enc = (encRes as any).value;
|
|
267
|
+
|
|
268
|
+
const texResA = proxyDevice.createTexture({
|
|
269
|
+
size: { width: 4, height: 4 },
|
|
270
|
+
format: 'rgba8unorm',
|
|
271
|
+
usage: 9,
|
|
272
|
+
});
|
|
273
|
+
const texResB = proxyDevice.createTexture({
|
|
274
|
+
size: { width: 4, height: 4 },
|
|
275
|
+
format: 'rgba8unorm',
|
|
276
|
+
usage: 8,
|
|
277
|
+
});
|
|
278
|
+
expect(texResA.ok).toBe(true);
|
|
279
|
+
expect(texResB.ok).toBe(true);
|
|
280
|
+
const texA = (texResA as any).value;
|
|
281
|
+
const texB = (texResB as any).value;
|
|
282
|
+
|
|
283
|
+
enc.copyTextureToTexture(
|
|
284
|
+
{ texture: texA, mipLevel: 0 },
|
|
285
|
+
{ texture: texB, mipLevel: 0 },
|
|
286
|
+
{ width: 4, height: 4 },
|
|
287
|
+
);
|
|
288
|
+
const finishRes = enc.finish();
|
|
289
|
+
expect(finishRes.ok).toBe(true);
|
|
290
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
291
|
+
|
|
292
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
293
|
+
const copyEvents = findEventsOfKind(events, 'copyTextureToTexture');
|
|
294
|
+
expect(copyEvents.length).toBe(1);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('clearBuffer pushEvent produces event kind in tape', async () => {
|
|
298
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
299
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
300
|
+
expect(encRes.ok).toBe(true);
|
|
301
|
+
const enc = (encRes as any).value;
|
|
302
|
+
|
|
303
|
+
const bufRes = proxyDevice.createBuffer({ size: 64, usage: 1 });
|
|
304
|
+
expect(bufRes.ok).toBe(true);
|
|
305
|
+
const buf = (bufRes as any).value;
|
|
306
|
+
|
|
307
|
+
enc.clearBuffer(buf, 0, 64);
|
|
308
|
+
const finishRes = enc.finish();
|
|
309
|
+
expect(finishRes.ok).toBe(true);
|
|
310
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
311
|
+
|
|
312
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
313
|
+
const clearEvents = findEventsOfKind(events, 'clearBuffer');
|
|
314
|
+
expect(clearEvents.length).toBe(1);
|
|
315
|
+
const ev = clearEvents[0] as RhiCallEventClearBuffer;
|
|
316
|
+
expect(ev.handleId).toBeDefined();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('setViewport pushEvent produces event kind in tape', async () => {
|
|
320
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
321
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
322
|
+
expect(encRes.ok).toBe(true);
|
|
323
|
+
const enc = (encRes as any).value;
|
|
324
|
+
|
|
325
|
+
const pass = enc.beginRenderPass({
|
|
326
|
+
colorAttachments: [],
|
|
327
|
+
});
|
|
328
|
+
pass.setViewport(0, 0, 800, 600, 0, 1);
|
|
329
|
+
pass.end();
|
|
330
|
+
const finishRes = enc.finish();
|
|
331
|
+
expect(finishRes.ok).toBe(true);
|
|
332
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
333
|
+
|
|
334
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
335
|
+
const vpEvents = findEventsOfKind(events, 'setViewport');
|
|
336
|
+
expect(vpEvents.length).toBe(1);
|
|
337
|
+
const ev = vpEvents[0] as RhiCallEventSetViewport;
|
|
338
|
+
expect(ev.w).toBe(800);
|
|
339
|
+
expect(ev.h).toBe(600);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('setScissorRect pushEvent produces event kind in tape', async () => {
|
|
343
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
344
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
345
|
+
expect(encRes.ok).toBe(true);
|
|
346
|
+
const enc = (encRes as any).value;
|
|
347
|
+
|
|
348
|
+
const pass = enc.beginRenderPass({
|
|
349
|
+
colorAttachments: [],
|
|
350
|
+
});
|
|
351
|
+
pass.setScissorRect(0, 0, 800, 600);
|
|
352
|
+
pass.end();
|
|
353
|
+
const finishRes = enc.finish();
|
|
354
|
+
expect(finishRes.ok).toBe(true);
|
|
355
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
356
|
+
|
|
357
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
358
|
+
const scEvents = findEventsOfKind(events, 'setScissorRect');
|
|
359
|
+
expect(scEvents.length).toBe(1);
|
|
360
|
+
const ev = scEvents[0] as RhiCallEventSetScissorRect;
|
|
361
|
+
expect(ev.w).toBe(800);
|
|
362
|
+
expect(ev.h).toBe(600);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// ============================================================================
|
|
367
|
+
// AC-04: 3 encoder-level debug group commands
|
|
368
|
+
// ============================================================================
|
|
369
|
+
|
|
370
|
+
describe('w8: AC-04 encoder-level debug group (3 total)', () => {
|
|
371
|
+
it('pushDebugGroup on encoder produces event kind in tape', async () => {
|
|
372
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
373
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
374
|
+
expect(encRes.ok).toBe(true);
|
|
375
|
+
const enc = (encRes as any).value;
|
|
376
|
+
|
|
377
|
+
enc.pushDebugGroup('test-group');
|
|
378
|
+
const finishRes = enc.finish();
|
|
379
|
+
expect(finishRes.ok).toBe(true);
|
|
380
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
381
|
+
|
|
382
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
383
|
+
const pdgEvents = findEventsOfKind(events, 'pushDebugGroup');
|
|
384
|
+
expect(pdgEvents.length).toBe(1);
|
|
385
|
+
const ev = pdgEvents[0] as RhiCallEventPushDebugGroup;
|
|
386
|
+
expect(ev.groupLabel).toBe('test-group');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('popDebugGroup on encoder produces event kind in tape', async () => {
|
|
390
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
391
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
392
|
+
expect(encRes.ok).toBe(true);
|
|
393
|
+
const enc = (encRes as any).value;
|
|
394
|
+
|
|
395
|
+
enc.popDebugGroup();
|
|
396
|
+
const finishRes = enc.finish();
|
|
397
|
+
expect(finishRes.ok).toBe(true);
|
|
398
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
399
|
+
|
|
400
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
401
|
+
const pdgEvents = findEventsOfKind(events, 'popDebugGroup');
|
|
402
|
+
expect(pdgEvents.length).toBe(1);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('insertDebugMarker on encoder produces event kind in tape', async () => {
|
|
406
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
407
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
408
|
+
expect(encRes.ok).toBe(true);
|
|
409
|
+
const enc = (encRes as any).value;
|
|
410
|
+
|
|
411
|
+
enc.insertDebugMarker('marker-1');
|
|
412
|
+
const finishRes = enc.finish();
|
|
413
|
+
expect(finishRes.ok).toBe(true);
|
|
414
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
415
|
+
|
|
416
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
417
|
+
const idmEvents = findEventsOfKind(events, 'insertDebugMarker');
|
|
418
|
+
expect(idmEvents.length).toBe(1);
|
|
419
|
+
const ev = idmEvents[0] as RhiCallEventInsertDebugMarker;
|
|
420
|
+
expect(ev.markerLabel).toBe('marker-1');
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// AC-05: 6 new events
|
|
426
|
+
// ============================================================================
|
|
427
|
+
|
|
428
|
+
describe('w8: AC-05 new events (6 total)', () => {
|
|
429
|
+
it('setBlendConstant pushEvent produces event kind in tape', async () => {
|
|
430
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
431
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
432
|
+
expect(encRes.ok).toBe(true);
|
|
433
|
+
const enc = (encRes as any).value;
|
|
434
|
+
|
|
435
|
+
const pass = enc.beginRenderPass({
|
|
436
|
+
colorAttachments: [],
|
|
437
|
+
});
|
|
438
|
+
pass.setBlendConstant([0.1, 0.2, 0.3, 0.4]);
|
|
439
|
+
pass.end();
|
|
440
|
+
const finishRes = enc.finish();
|
|
441
|
+
expect(finishRes.ok).toBe(true);
|
|
442
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
443
|
+
|
|
444
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
445
|
+
const bcEvents = findEventsOfKind(events, 'setBlendConstant');
|
|
446
|
+
expect(bcEvents.length).toBe(1);
|
|
447
|
+
const ev = bcEvents[0] as RhiCallEventSetBlendConstant;
|
|
448
|
+
expect(ev.color).toEqual([0.1, 0.2, 0.3, 0.4]);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('drawIndirect pushEvent produces event kind + indirectBufferHandleId in tape', async () => {
|
|
452
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
453
|
+
// Create an indirect buffer
|
|
454
|
+
const bufRes = proxyDevice.createBuffer({ size: 20, usage: 256 }); // INDIRECT
|
|
455
|
+
expect(bufRes.ok).toBe(true);
|
|
456
|
+
const indirectBuf = (bufRes as any).value;
|
|
457
|
+
|
|
458
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
459
|
+
expect(encRes.ok).toBe(true);
|
|
460
|
+
const enc = (encRes as any).value;
|
|
461
|
+
|
|
462
|
+
const pass = enc.beginRenderPass({
|
|
463
|
+
colorAttachments: [],
|
|
464
|
+
});
|
|
465
|
+
pass.drawIndirect(indirectBuf, 0);
|
|
466
|
+
pass.end();
|
|
467
|
+
const finishRes = enc.finish();
|
|
468
|
+
expect(finishRes.ok).toBe(true);
|
|
469
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
470
|
+
|
|
471
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
472
|
+
const diEvents = findEventsOfKind(events, 'drawIndirect');
|
|
473
|
+
expect(diEvents.length).toBe(1);
|
|
474
|
+
const ev = diEvents[0] as RhiCallEventDrawIndirect;
|
|
475
|
+
expect(ev.indirectBufferHandleId).toBeDefined();
|
|
476
|
+
expect(ev.indirectBufferHandleId.length).toBeGreaterThan(0);
|
|
477
|
+
expect(ev.indirectOffset).toBe(0);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('drawIndexedIndirect pushEvent produces event kind + indirectBufferHandleId in tape', async () => {
|
|
481
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
482
|
+
const bufRes = proxyDevice.createBuffer({ size: 20, usage: 256 }); // INDIRECT
|
|
483
|
+
expect(bufRes.ok).toBe(true);
|
|
484
|
+
const indirectBuf = (bufRes as any).value;
|
|
485
|
+
|
|
486
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
487
|
+
expect(encRes.ok).toBe(true);
|
|
488
|
+
const enc = (encRes as any).value;
|
|
489
|
+
|
|
490
|
+
const pass = enc.beginRenderPass({
|
|
491
|
+
colorAttachments: [],
|
|
492
|
+
});
|
|
493
|
+
pass.drawIndexedIndirect(indirectBuf, 0);
|
|
494
|
+
pass.end();
|
|
495
|
+
const finishRes = enc.finish();
|
|
496
|
+
expect(finishRes.ok).toBe(true);
|
|
497
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
498
|
+
|
|
499
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
500
|
+
const diiEvents = findEventsOfKind(events, 'drawIndexedIndirect');
|
|
501
|
+
expect(diiEvents.length).toBe(1);
|
|
502
|
+
const ev = diiEvents[0] as RhiCallEventDrawIndexedIndirect;
|
|
503
|
+
expect(ev.indirectBufferHandleId).toBeDefined();
|
|
504
|
+
expect(ev.indirectBufferHandleId.length).toBeGreaterThan(0);
|
|
505
|
+
expect(ev.indirectOffset).toBe(0);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('passPushDebugGroup pushEvent produces event kind in tape', async () => {
|
|
509
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
510
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
511
|
+
expect(encRes.ok).toBe(true);
|
|
512
|
+
const enc = (encRes as any).value;
|
|
513
|
+
|
|
514
|
+
const pass = enc.beginRenderPass({
|
|
515
|
+
colorAttachments: [],
|
|
516
|
+
});
|
|
517
|
+
pass.pushDebugGroup('pass-group');
|
|
518
|
+
pass.end();
|
|
519
|
+
const finishRes = enc.finish();
|
|
520
|
+
expect(finishRes.ok).toBe(true);
|
|
521
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
522
|
+
|
|
523
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
524
|
+
const ppdgEvents = findEventsOfKind(events, 'passPushDebugGroup');
|
|
525
|
+
expect(ppdgEvents.length).toBe(1);
|
|
526
|
+
const ev = ppdgEvents[0] as RhiCallEventPassPushDebugGroup;
|
|
527
|
+
expect(ev.groupLabel).toBe('pass-group');
|
|
528
|
+
expect(ev.passHandleId).toBeDefined();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
it('passPopDebugGroup pushEvent produces event kind in tape', async () => {
|
|
532
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
533
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
534
|
+
expect(encRes.ok).toBe(true);
|
|
535
|
+
const enc = (encRes as any).value;
|
|
536
|
+
|
|
537
|
+
const pass = enc.beginRenderPass({
|
|
538
|
+
colorAttachments: [],
|
|
539
|
+
});
|
|
540
|
+
pass.popDebugGroup();
|
|
541
|
+
pass.end();
|
|
542
|
+
const finishRes = enc.finish();
|
|
543
|
+
expect(finishRes.ok).toBe(true);
|
|
544
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
545
|
+
|
|
546
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
547
|
+
const ppdgEvents = findEventsOfKind(events, 'passPopDebugGroup');
|
|
548
|
+
expect(ppdgEvents.length).toBe(1);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
it('passInsertDebugMarker pushEvent produces event kind in tape', async () => {
|
|
552
|
+
const { debugInst, proxyDevice } = await buildMockAndArm();
|
|
553
|
+
const encRes = proxyDevice.createCommandEncoder();
|
|
554
|
+
expect(encRes.ok).toBe(true);
|
|
555
|
+
const enc = (encRes as any).value;
|
|
556
|
+
|
|
557
|
+
const pass = enc.beginRenderPass({
|
|
558
|
+
colorAttachments: [],
|
|
559
|
+
});
|
|
560
|
+
pass.insertDebugMarker('pass-marker');
|
|
561
|
+
pass.end();
|
|
562
|
+
const finishRes = enc.finish();
|
|
563
|
+
expect(finishRes.ok).toBe(true);
|
|
564
|
+
proxyDevice.queue.submit([finishRes.value]);
|
|
565
|
+
|
|
566
|
+
const events = finalizeAndGetEvents(debugInst);
|
|
567
|
+
const pidmEvents = findEventsOfKind(events, 'passInsertDebugMarker');
|
|
568
|
+
expect(pidmEvents.length).toBe(1);
|
|
569
|
+
const ev = pidmEvents[0] as RhiCallEventPassInsertDebugMarker;
|
|
570
|
+
expect(ev.markerLabel).toBe('pass-marker');
|
|
571
|
+
expect(ev.passHandleId).toBeDefined();
|
|
572
|
+
});
|
|
573
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createShaderModule, rhi } from '@forgeax/engine-rhi-null';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { attachRecorder } from '../index';
|
|
4
|
+
import { decodeTape } from '../protocol/codec';
|
|
5
|
+
|
|
6
|
+
describe('RecorderSession real RHI consumer', () => {
|
|
7
|
+
it('captures one steady frame into a strict v7 artifact', async () => {
|
|
8
|
+
const attached = attachRecorder({ rhi, createShaderModule });
|
|
9
|
+
expect(attached.ok).toBe(true);
|
|
10
|
+
if (!attached.ok) return;
|
|
11
|
+
|
|
12
|
+
const adapter = await attached.value.backend.rhi.requestAdapter();
|
|
13
|
+
expect(adapter.ok).toBe(true);
|
|
14
|
+
if (!adapter.ok) return;
|
|
15
|
+
const device = await adapter.value.requestDevice();
|
|
16
|
+
expect(device.ok).toBe(true);
|
|
17
|
+
if (!device.ok) return;
|
|
18
|
+
|
|
19
|
+
const capture = attached.value.captureFrame();
|
|
20
|
+
expect((await attached.value.frameBoundary()).ok).toBe(true);
|
|
21
|
+
expect((await attached.value.frameBoundary()).ok).toBe(true);
|
|
22
|
+
const result = await capture;
|
|
23
|
+
expect(result.ok).toBe(true);
|
|
24
|
+
if (!result.ok) return;
|
|
25
|
+
const decoded = decodeTape(result.value.bytes);
|
|
26
|
+
expect(decoded.ok).toBe(true);
|
|
27
|
+
if (!decoded.ok) return;
|
|
28
|
+
expect(decoded.value.header.formatVersion).toBe(7);
|
|
29
|
+
expect(decoded.value.header.eventCount).toBe(1);
|
|
30
|
+
expect(decoded.value.events[0]?.kind).toBe('frameMark');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { RhiInstance } from '@forgeax/engine-rhi';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { attachRecorder, type RecordableBackend } from '../recorder/session';
|
|
4
|
+
|
|
5
|
+
function backend(): RecordableBackend {
|
|
6
|
+
const rhi = {
|
|
7
|
+
requestAdapter: vi.fn(() => Promise.resolve({ ok: false as const, error: {} })),
|
|
8
|
+
} as unknown as RhiInstance;
|
|
9
|
+
return {
|
|
10
|
+
rhi,
|
|
11
|
+
createShaderModule: vi.fn(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('RecorderSession contract', () => {
|
|
16
|
+
it('accepts one capture and rejects a concurrent request', async () => {
|
|
17
|
+
const attached = attachRecorder(backend());
|
|
18
|
+
expect(attached.ok).toBe(true);
|
|
19
|
+
if (!attached.ok) return;
|
|
20
|
+
|
|
21
|
+
const capture = attached.value.captureFrame();
|
|
22
|
+
const busy = await attached.value.captureFrame();
|
|
23
|
+
expect(busy).toMatchObject({ ok: false, error: { code: 'capture-busy' } });
|
|
24
|
+
|
|
25
|
+
await attached.value.frameBoundary();
|
|
26
|
+
await attached.value.frameBoundary();
|
|
27
|
+
const result = await capture;
|
|
28
|
+
expect(result.ok).toBe(true);
|
|
29
|
+
if (result.ok) expect(result.value.bytes.byteLength).toBeGreaterThan(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('does not retain frame work before capture is armed', () => {
|
|
33
|
+
const attached = attachRecorder(backend());
|
|
34
|
+
expect(attached.ok).toBe(true);
|
|
35
|
+
if (!attached.ok) return;
|
|
36
|
+
|
|
37
|
+
expect('getEvents' in attached.value.backend.rhi).toBe(false);
|
|
38
|
+
expect('getBlobPool' in attached.value.backend.rhi).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('keeps the wrapped shader factory on the explicit RHI singleton', () => {
|
|
42
|
+
const attached = attachRecorder(backend());
|
|
43
|
+
expect(attached.ok).toBe(true);
|
|
44
|
+
if (!attached.ok) return;
|
|
45
|
+
|
|
46
|
+
expect(
|
|
47
|
+
(attached.value.backend.rhi as unknown as { createShaderModule: unknown }).createShaderModule,
|
|
48
|
+
).toBe(attached.value.backend.createShaderModule);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('resolves an abort as one terminal structured result', async () => {
|
|
52
|
+
const attached = attachRecorder(backend());
|
|
53
|
+
expect(attached.ok).toBe(true);
|
|
54
|
+
if (!attached.ok) return;
|
|
55
|
+
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const capture = attached.value.captureFrame({ signal: controller.signal });
|
|
58
|
+
controller.abort();
|
|
59
|
+
const result = await capture;
|
|
60
|
+
expect(result).toMatchObject({ ok: false, error: { code: 'capture-unavailable' } });
|
|
61
|
+
expect((await attached.value.frameBoundary()).ok).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|