@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,66 @@
|
|
|
1
|
+
import type { RhiDevice } from '@forgeax/engine-rhi';
|
|
2
|
+
import { createShaderModule, rhi } from '@forgeax/engine-rhi-null';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import type { BootstrapResource, Tape } from '../protocol/types';
|
|
5
|
+
import type { ReplayBackend } from '../replay/session';
|
|
6
|
+
import { openReplay } from '../replay/session';
|
|
7
|
+
|
|
8
|
+
const buffer: BootstrapResource = {
|
|
9
|
+
handleId: 'buffer:readback',
|
|
10
|
+
kind: 'buffer',
|
|
11
|
+
create: {
|
|
12
|
+
kind: 'createBuffer',
|
|
13
|
+
handleId: 'buffer:readback',
|
|
14
|
+
desc: { size: 32, usage: 8 },
|
|
15
|
+
},
|
|
16
|
+
initialData: [],
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const tape: Tape = {
|
|
20
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
21
|
+
bootstrap: [buffer],
|
|
22
|
+
events: [],
|
|
23
|
+
blobs: [],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
async function backendWithDestroySpy(): Promise<{
|
|
27
|
+
backend: ReplayBackend;
|
|
28
|
+
destroy: ReturnType<typeof vi.fn>;
|
|
29
|
+
}> {
|
|
30
|
+
const adapter = await rhi.requestAdapter();
|
|
31
|
+
if (!adapter.ok) throw new Error(adapter.error.hint);
|
|
32
|
+
const result = await adapter.value.requestDevice();
|
|
33
|
+
if (!result.ok) throw new Error(result.error.hint);
|
|
34
|
+
const base = result.value;
|
|
35
|
+
const destroy = vi.fn((value) => base.destroyBuffer(value));
|
|
36
|
+
const device = Object.create(base) as RhiDevice;
|
|
37
|
+
Object.defineProperty(device, 'destroyBuffer', { configurable: true, value: destroy });
|
|
38
|
+
return { backend: { device, createShaderModule }, destroy };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('Replay readback cleanup', () => {
|
|
42
|
+
it('cleans staging resources when map/readback fails', async () => {
|
|
43
|
+
const { backend, destroy } = await backendWithDestroySpy();
|
|
44
|
+
const replay = await openReplay(tape, backend);
|
|
45
|
+
expect(replay.ok).toBe(true);
|
|
46
|
+
if (!replay.ok) return;
|
|
47
|
+
|
|
48
|
+
const readback = await replay.value.readResource('buffer:readback', { offset: 4, size: 8 });
|
|
49
|
+
expect(readback.ok).toBe(false);
|
|
50
|
+
if (!readback.ok) expect(readback.error.code).toBe('readback-failed');
|
|
51
|
+
expect(destroy).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('rejects a range that escapes the recorded buffer before GPU work', async () => {
|
|
55
|
+
const replay = await openReplay(
|
|
56
|
+
tape,
|
|
57
|
+
await backendWithDestroySpy().then((value) => value.backend),
|
|
58
|
+
);
|
|
59
|
+
expect(replay.ok).toBe(true);
|
|
60
|
+
if (!replay.ok) return;
|
|
61
|
+
|
|
62
|
+
const readback = await replay.value.readResource('buffer:readback', { offset: 24, size: 16 });
|
|
63
|
+
expect(readback.ok).toBe(false);
|
|
64
|
+
if (!readback.ok) expect(readback.error.code).toBe('readback-failed');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import type { RhiInstance } from '@forgeax/engine-rhi';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import type { BootstrapResource, Tape, TapeBlob } from '../protocol/types';
|
|
4
|
+
import type { ReplayBackend } from '../replay/session';
|
|
5
|
+
import { openReplay } from '../replay/session';
|
|
6
|
+
import { READBACK_MATRIX_CASES } from './readback-matrix-fixture';
|
|
7
|
+
|
|
8
|
+
interface DawnPack {
|
|
9
|
+
readonly rhi: RhiInstance;
|
|
10
|
+
readonly createShaderModule: ReplayBackend['createShaderModule'];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function loadDawn(): Promise<DawnPack> {
|
|
14
|
+
return (await import('@forgeax/engine-rhi-webgpu')) as unknown as DawnPack;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const SKIP_DAWN = process.env.FORGEAX_SKIP_DAWN === '1';
|
|
18
|
+
|
|
19
|
+
function colorResource(): BootstrapResource {
|
|
20
|
+
return {
|
|
21
|
+
handleId: 'texture:color',
|
|
22
|
+
kind: 'texture',
|
|
23
|
+
create: {
|
|
24
|
+
kind: 'createTexture',
|
|
25
|
+
handleId: 'texture:color',
|
|
26
|
+
desc: {
|
|
27
|
+
size: { width: 4, height: 4, depthOrArrayLayers: 1 },
|
|
28
|
+
format: 'rgba8unorm',
|
|
29
|
+
dimension: '2d',
|
|
30
|
+
mipLevelCount: 1,
|
|
31
|
+
sampleCount: 1,
|
|
32
|
+
usage: 1,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
initialData: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function depthResource(
|
|
40
|
+
handleId: string,
|
|
41
|
+
format: 'depth24plus' | 'depth24plus-stencil8' | 'depth32float',
|
|
42
|
+
): BootstrapResource {
|
|
43
|
+
return {
|
|
44
|
+
handleId,
|
|
45
|
+
kind: 'texture',
|
|
46
|
+
create: {
|
|
47
|
+
kind: 'createTexture',
|
|
48
|
+
handleId,
|
|
49
|
+
desc: {
|
|
50
|
+
size: { width: 4, height: 4, depthOrArrayLayers: 1 },
|
|
51
|
+
format,
|
|
52
|
+
dimension: '2d',
|
|
53
|
+
mipLevelCount: 1,
|
|
54
|
+
sampleCount: 1,
|
|
55
|
+
usage: 0x15,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
initialData: [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function matrixTextureResource(): BootstrapResource {
|
|
63
|
+
return {
|
|
64
|
+
handleId: 'texture:matrix',
|
|
65
|
+
kind: 'texture',
|
|
66
|
+
create: {
|
|
67
|
+
kind: 'createTexture',
|
|
68
|
+
handleId: 'texture:matrix',
|
|
69
|
+
desc: {
|
|
70
|
+
size: { width: 2, height: 2, depthOrArrayLayers: 12 },
|
|
71
|
+
format: 'rgba8unorm',
|
|
72
|
+
dimension: '2d',
|
|
73
|
+
mipLevelCount: 2,
|
|
74
|
+
sampleCount: 1,
|
|
75
|
+
usage: 0x1f,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
initialData: [{ hash: 'matrix-texture', byteOffset: 0, byteLength: 240 }],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function matrixBlob(): TapeBlob {
|
|
83
|
+
return {
|
|
84
|
+
hash: 'matrix-texture',
|
|
85
|
+
bytes: Uint8Array.from({ length: 240 }, (_, index) => index),
|
|
86
|
+
compression: 'none',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe.skipIf(SKIP_DAWN)('Dawn replay-owned readback matrix', () => {
|
|
91
|
+
it('enumerates the canonical descriptor and subresource matrix', () => {
|
|
92
|
+
expect(READBACK_MATRIX_CASES).toEqual([
|
|
93
|
+
{ format: 'rgba8unorm', dimension: '2d', aspect: 'all' },
|
|
94
|
+
{ format: 'depth32float', dimension: '2d', aspect: 'depth-only' },
|
|
95
|
+
{ format: 'depth24plus', dimension: '2d', aspect: 'depth-only' },
|
|
96
|
+
{ format: 'depth24plus-stencil8', dimension: '2d', aspect: 'stencil-only' },
|
|
97
|
+
{ format: 'rgba8unorm', dimension: '2d-array', aspect: 'all' },
|
|
98
|
+
{ format: 'rgba8unorm', dimension: 'cube', aspect: 'all' },
|
|
99
|
+
{ format: 'rgba8unorm', dimension: 'cube-array', aspect: 'all' },
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('reads a color subresource on a fresh Dawn device', async () => {
|
|
104
|
+
const pack = await loadDawn();
|
|
105
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
106
|
+
expect(adapter.ok).toBe(true);
|
|
107
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
108
|
+
const device = await adapter.value.requestDevice();
|
|
109
|
+
expect(device.ok).toBe(true);
|
|
110
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
111
|
+
|
|
112
|
+
const tape: Tape = {
|
|
113
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
114
|
+
bootstrap: [colorResource()],
|
|
115
|
+
events: [],
|
|
116
|
+
blobs: [],
|
|
117
|
+
};
|
|
118
|
+
const replay = await openReplay(tape, {
|
|
119
|
+
device: device.value,
|
|
120
|
+
createShaderModule: pack.createShaderModule,
|
|
121
|
+
});
|
|
122
|
+
expect(replay.ok).toBe(true);
|
|
123
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
124
|
+
const pixels = await replay.value.readResource('texture:color', {
|
|
125
|
+
mipLevel: 0,
|
|
126
|
+
arrayLayer: 0,
|
|
127
|
+
aspect: 'all',
|
|
128
|
+
});
|
|
129
|
+
expect(pixels.ok).toBe(true);
|
|
130
|
+
if (!pixels.ok) throw new Error(pixels.error.hint);
|
|
131
|
+
expect(pixels.value.bytes.byteLength).toBe(4 * 4 * 4);
|
|
132
|
+
expect(pixels.value.provenance).toEqual({
|
|
133
|
+
generation: replay.value.generation,
|
|
134
|
+
resourceId: 'texture:color',
|
|
135
|
+
subresource: { mipLevel: 0, arrayLayer: 0, aspect: 'all' },
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('reads a known buffer range through the replay owner', async () => {
|
|
140
|
+
const pack = await loadDawn();
|
|
141
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
142
|
+
expect(adapter.ok).toBe(true);
|
|
143
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
144
|
+
const device = await adapter.value.requestDevice();
|
|
145
|
+
expect(device.ok).toBe(true);
|
|
146
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
147
|
+
const replay = await openReplay(
|
|
148
|
+
{
|
|
149
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 1 },
|
|
150
|
+
bootstrap: [
|
|
151
|
+
{
|
|
152
|
+
handleId: 'buffer:known',
|
|
153
|
+
kind: 'buffer',
|
|
154
|
+
create: {
|
|
155
|
+
kind: 'createBuffer',
|
|
156
|
+
handleId: 'buffer:known',
|
|
157
|
+
desc: { size: 8, usage: 0x0c },
|
|
158
|
+
},
|
|
159
|
+
initialData: [{ hash: 'buffer-known', byteOffset: 0, byteLength: 8 }],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
events: [],
|
|
163
|
+
blobs: [
|
|
164
|
+
{
|
|
165
|
+
hash: 'buffer-known',
|
|
166
|
+
bytes: Uint8Array.from([0, 1, 2, 3, 4, 5, 6, 7]),
|
|
167
|
+
compression: 'none',
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
{ device: device.value, createShaderModule: pack.createShaderModule },
|
|
172
|
+
);
|
|
173
|
+
expect(replay.ok).toBe(true);
|
|
174
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
175
|
+
const result = await replay.value.readResource('buffer:known', { offset: 2, size: 4 });
|
|
176
|
+
expect(result.ok).toBe(true);
|
|
177
|
+
if (!result.ok) throw new Error(result.error.hint);
|
|
178
|
+
expect([...result.value.bytes]).toEqual([2, 3, 4, 5]);
|
|
179
|
+
expect(result.value.provenance.subresource).toEqual({ offset: 2, size: 4 });
|
|
180
|
+
await replay.value.dispose();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('reads array, cube, cube-array, and view-local mip/layer subresources', async () => {
|
|
184
|
+
const pack = await loadDawn();
|
|
185
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
186
|
+
expect(adapter.ok).toBe(true);
|
|
187
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
188
|
+
const device = await adapter.value.requestDevice();
|
|
189
|
+
expect(device.ok).toBe(true);
|
|
190
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
191
|
+
const view = (handleId: string, dimension: string, baseArrayLayer: number, count: number) => ({
|
|
192
|
+
handleId,
|
|
193
|
+
kind: 'texture-view' as const,
|
|
194
|
+
create: {
|
|
195
|
+
kind: 'createTextureView',
|
|
196
|
+
sourceHandleId: 'texture:matrix',
|
|
197
|
+
resultHandleId: handleId,
|
|
198
|
+
desc: {
|
|
199
|
+
dimension,
|
|
200
|
+
baseMipLevel: 1,
|
|
201
|
+
mipLevelCount: 1,
|
|
202
|
+
baseArrayLayer,
|
|
203
|
+
arrayLayerCount: count,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
initialData: [],
|
|
207
|
+
});
|
|
208
|
+
const replay = await openReplay(
|
|
209
|
+
{
|
|
210
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 1 },
|
|
211
|
+
bootstrap: [
|
|
212
|
+
matrixTextureResource(),
|
|
213
|
+
view('view:array', '2d-array', 4, 2),
|
|
214
|
+
view('view:cube', 'cube', 0, 6),
|
|
215
|
+
view('view:cube-array', 'cube-array', 0, 12),
|
|
216
|
+
],
|
|
217
|
+
events: [],
|
|
218
|
+
blobs: [matrixBlob()],
|
|
219
|
+
},
|
|
220
|
+
{ device: device.value, createShaderModule: pack.createShaderModule },
|
|
221
|
+
);
|
|
222
|
+
expect(replay.ok).toBe(true);
|
|
223
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
224
|
+
const arrayResult = await replay.value.readResource('view:array', {
|
|
225
|
+
mipLevel: 0,
|
|
226
|
+
arrayLayer: 0,
|
|
227
|
+
aspect: 'all',
|
|
228
|
+
});
|
|
229
|
+
expect(arrayResult.ok).toBe(true);
|
|
230
|
+
if (!arrayResult.ok) throw new Error(arrayResult.error.hint);
|
|
231
|
+
expect(arrayResult.value.width).toBe(1);
|
|
232
|
+
expect(arrayResult.value.height).toBe(1);
|
|
233
|
+
expect([...arrayResult.value.bytes]).toEqual([96, 97, 98, 99]);
|
|
234
|
+
const cubeResult = await replay.value.readResource('view:cube', {
|
|
235
|
+
mipLevel: 0,
|
|
236
|
+
arrayLayer: 5,
|
|
237
|
+
aspect: 'all',
|
|
238
|
+
});
|
|
239
|
+
expect(cubeResult.ok).toBe(true);
|
|
240
|
+
if (!cubeResult.ok) throw new Error(cubeResult.error.hint);
|
|
241
|
+
expect(cubeResult.value.provenance.subresource).toEqual({
|
|
242
|
+
mipLevel: 0,
|
|
243
|
+
arrayLayer: 5,
|
|
244
|
+
aspect: 'all',
|
|
245
|
+
});
|
|
246
|
+
const cubeArrayResult = await replay.value.readResource('view:cube-array', {
|
|
247
|
+
mipLevel: 0,
|
|
248
|
+
arrayLayer: 11,
|
|
249
|
+
aspect: 'all',
|
|
250
|
+
});
|
|
251
|
+
expect(cubeArrayResult.ok).toBe(true);
|
|
252
|
+
if (!cubeArrayResult.ok) throw new Error(cubeArrayResult.error.hint);
|
|
253
|
+
expect([...cubeArrayResult.value.bytes]).toEqual([236, 237, 238, 239]);
|
|
254
|
+
await replay.value.dispose();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('reads depth32float through the direct depth aspect path', async () => {
|
|
258
|
+
const pack = await loadDawn();
|
|
259
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
260
|
+
expect(adapter.ok).toBe(true);
|
|
261
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
262
|
+
const device = await adapter.value.requestDevice();
|
|
263
|
+
expect(device.ok).toBe(true);
|
|
264
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
265
|
+
const replay = await openReplay(
|
|
266
|
+
{
|
|
267
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
268
|
+
bootstrap: [depthResource('texture:depth32', 'depth32float')],
|
|
269
|
+
events: [],
|
|
270
|
+
blobs: [],
|
|
271
|
+
},
|
|
272
|
+
{ device: device.value, createShaderModule: pack.createShaderModule },
|
|
273
|
+
);
|
|
274
|
+
expect(replay.ok).toBe(true);
|
|
275
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
276
|
+
const result = await replay.value.readResource('texture:depth32', {
|
|
277
|
+
mipLevel: 0,
|
|
278
|
+
arrayLayer: 0,
|
|
279
|
+
aspect: 'depth-only',
|
|
280
|
+
});
|
|
281
|
+
expect(result.ok).toBe(true);
|
|
282
|
+
if (!result.ok) throw new Error(result.error.hint);
|
|
283
|
+
expect(result.value.bytes.byteLength).toBe(4 * 4 * 4);
|
|
284
|
+
expect(result.value.provenance.subresource).toEqual({
|
|
285
|
+
mipLevel: 0,
|
|
286
|
+
arrayLayer: 0,
|
|
287
|
+
aspect: 'depth-only',
|
|
288
|
+
});
|
|
289
|
+
await replay.value.dispose();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('reads depth24plus through the replay-owned depth-to-color blit', async () => {
|
|
293
|
+
const pack = await loadDawn();
|
|
294
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
295
|
+
expect(adapter.ok).toBe(true);
|
|
296
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
297
|
+
const device = await adapter.value.requestDevice();
|
|
298
|
+
expect(device.ok).toBe(true);
|
|
299
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
300
|
+
|
|
301
|
+
const replay = await openReplay(
|
|
302
|
+
{
|
|
303
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
304
|
+
bootstrap: [depthResource('texture:depth24plus', 'depth24plus')],
|
|
305
|
+
events: [],
|
|
306
|
+
blobs: [],
|
|
307
|
+
},
|
|
308
|
+
{ device: device.value, createShaderModule: pack.createShaderModule },
|
|
309
|
+
);
|
|
310
|
+
expect(replay.ok).toBe(true);
|
|
311
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
312
|
+
const pixels = await replay.value.readResource('texture:depth24plus', {
|
|
313
|
+
mipLevel: 0,
|
|
314
|
+
arrayLayer: 0,
|
|
315
|
+
aspect: 'depth-only',
|
|
316
|
+
});
|
|
317
|
+
expect(pixels.ok).toBe(true);
|
|
318
|
+
if (!pixels.ok) throw new Error(pixels.error.hint);
|
|
319
|
+
expect(pixels.value.bytes.byteLength).toBe(4 * 4 * 4);
|
|
320
|
+
expect([...new Float32Array(pixels.value.bytes.buffer)]).toHaveLength(16);
|
|
321
|
+
expect(pixels.value.provenance.resourceId).toBe('texture:depth24plus');
|
|
322
|
+
expect(pixels.value.provenance.subresource).toEqual({
|
|
323
|
+
mipLevel: 0,
|
|
324
|
+
arrayLayer: 0,
|
|
325
|
+
aspect: 'depth-only',
|
|
326
|
+
});
|
|
327
|
+
await replay.value.dispose();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('reads both explicit planes on depth24plus-stencil8', async () => {
|
|
331
|
+
const pack = await loadDawn();
|
|
332
|
+
const adapter = await pack.rhi.requestAdapter();
|
|
333
|
+
expect(adapter.ok).toBe(true);
|
|
334
|
+
if (!adapter.ok) throw new Error(`Dawn admission failed: ${adapter.error.code}`);
|
|
335
|
+
const device = await adapter.value.requestDevice();
|
|
336
|
+
expect(device.ok).toBe(true);
|
|
337
|
+
if (!device.ok) throw new Error(`Dawn device admission failed: ${device.error.code}`);
|
|
338
|
+
|
|
339
|
+
const replay = await openReplay(
|
|
340
|
+
{
|
|
341
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
342
|
+
bootstrap: [depthResource('texture:stencil', 'depth24plus-stencil8')],
|
|
343
|
+
events: [],
|
|
344
|
+
blobs: [],
|
|
345
|
+
},
|
|
346
|
+
{ device: device.value, createShaderModule: pack.createShaderModule },
|
|
347
|
+
);
|
|
348
|
+
expect(replay.ok).toBe(true);
|
|
349
|
+
if (!replay.ok) throw new Error(replay.error.hint);
|
|
350
|
+
const depth = await replay.value.readResource('texture:stencil', {
|
|
351
|
+
mipLevel: 0,
|
|
352
|
+
arrayLayer: 0,
|
|
353
|
+
aspect: 'depth-only',
|
|
354
|
+
});
|
|
355
|
+
expect(depth.ok).toBe(true);
|
|
356
|
+
if (!depth.ok) throw new Error(depth.error.hint);
|
|
357
|
+
expect(depth.value.bytes.byteLength).toBe(4 * 4 * 4);
|
|
358
|
+
expect(depth.value.provenance.subresource).toEqual({
|
|
359
|
+
mipLevel: 0,
|
|
360
|
+
arrayLayer: 0,
|
|
361
|
+
aspect: 'depth-only',
|
|
362
|
+
});
|
|
363
|
+
const pixels = await replay.value.readResource('texture:stencil', {
|
|
364
|
+
mipLevel: 0,
|
|
365
|
+
arrayLayer: 0,
|
|
366
|
+
aspect: 'stencil-only',
|
|
367
|
+
});
|
|
368
|
+
expect(pixels.ok).toBe(true);
|
|
369
|
+
if (!pixels.ok) throw new Error(pixels.error.hint);
|
|
370
|
+
expect(pixels.value.bytes.byteLength).toBe(4 * 4);
|
|
371
|
+
expect(pixels.value.provenance.subresource).toEqual({
|
|
372
|
+
mipLevel: 0,
|
|
373
|
+
arrayLayer: 0,
|
|
374
|
+
aspect: 'stencil-only',
|
|
375
|
+
});
|
|
376
|
+
await replay.value.dispose();
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createShaderModule, rhi } from '@forgeax/engine-rhi-null';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import type { BootstrapResource, Tape } from '../protocol/types';
|
|
4
|
+
import type { ReplayReadbackResult } from '../replay/readback';
|
|
5
|
+
import type { TextureSubresource } from '../replay/session';
|
|
6
|
+
import { openReplay } from '../replay/session';
|
|
7
|
+
import { getTextureReadbackPlan } from '../replay/texture-format';
|
|
8
|
+
|
|
9
|
+
function textureResource(
|
|
10
|
+
handleId: string,
|
|
11
|
+
format: string,
|
|
12
|
+
dimension: string,
|
|
13
|
+
layers = 1,
|
|
14
|
+
): BootstrapResource {
|
|
15
|
+
return {
|
|
16
|
+
handleId,
|
|
17
|
+
kind: 'texture',
|
|
18
|
+
create: {
|
|
19
|
+
kind: 'createTexture',
|
|
20
|
+
handleId,
|
|
21
|
+
desc: {
|
|
22
|
+
size: { width: 4, height: 4, depthOrArrayLayers: layers },
|
|
23
|
+
format,
|
|
24
|
+
dimension,
|
|
25
|
+
mipLevelCount: 1,
|
|
26
|
+
sampleCount: 1,
|
|
27
|
+
usage: 1,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
initialData: [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function tape(bootstrap: readonly BootstrapResource[]): Tape {
|
|
35
|
+
return {
|
|
36
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
37
|
+
bootstrap,
|
|
38
|
+
events: [],
|
|
39
|
+
blobs: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function nullBackend() {
|
|
44
|
+
const adapter = await rhi.requestAdapter();
|
|
45
|
+
if (!adapter.ok) throw new Error(adapter.error.hint);
|
|
46
|
+
const device = await adapter.value.requestDevice();
|
|
47
|
+
if (!device.ok) throw new Error(device.error.hint);
|
|
48
|
+
return { device: device.value, createShaderModule };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('Replay readback format matrix', () => {
|
|
52
|
+
it.each([
|
|
53
|
+
['bgra8unorm', '2d'],
|
|
54
|
+
['depth24plus-stencil8', '2d'],
|
|
55
|
+
['rgba8unorm', '2d'],
|
|
56
|
+
['rgba8unorm-srgb', '2d'],
|
|
57
|
+
['rg16float', '2d'],
|
|
58
|
+
['rgba16float', '2d'],
|
|
59
|
+
['depth32float', '2d'],
|
|
60
|
+
['depth24plus', '2d'],
|
|
61
|
+
['rgba8unorm', 'cube'],
|
|
62
|
+
])('keeps %s/%s in the supported matrix', (format, dimension) => {
|
|
63
|
+
const plan = getTextureReadbackPlan({ format, dimension });
|
|
64
|
+
expect(plan.supported).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it.each([
|
|
68
|
+
['bc7-rgba-unorm', '2d'],
|
|
69
|
+
['rgba8unorm', '3d'],
|
|
70
|
+
])('returns an explicit unsupported plan for %s/%s', (format, dimension) => {
|
|
71
|
+
const plan = getTextureReadbackPlan({ format, dimension });
|
|
72
|
+
expect(plan.supported).toBe(false);
|
|
73
|
+
if (!plan.supported) expect(plan.reason.length).toBeGreaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns descriptor-aware readback-unsupported without allocating staging resources', async () => {
|
|
77
|
+
const result = await openReplay(
|
|
78
|
+
tape([textureResource('texture:compressed', 'bc7-rgba-unorm', '2d')]),
|
|
79
|
+
await nullBackend(),
|
|
80
|
+
);
|
|
81
|
+
expect(result.ok).toBe(true);
|
|
82
|
+
if (!result.ok) return;
|
|
83
|
+
|
|
84
|
+
const readback = await result.value.readResource('texture:compressed');
|
|
85
|
+
expect(readback.ok).toBe(false);
|
|
86
|
+
if (!readback.ok && readback.error.code === 'readback-unsupported') {
|
|
87
|
+
expect(readback.error.code).toBe('readback-unsupported');
|
|
88
|
+
expect(readback.error.detail?.format).toBe('bc7-rgba-unorm');
|
|
89
|
+
expect(readback.error.detail?.resourceId).toBe('texture:compressed');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('keeps texture subresource coordinates in the replay-owned request shape', () => {
|
|
94
|
+
const subresource: TextureSubresource = {
|
|
95
|
+
mipLevel: 1,
|
|
96
|
+
arrayLayer: 2,
|
|
97
|
+
aspect: 'stencil-only',
|
|
98
|
+
};
|
|
99
|
+
expect(subresource).toEqual({ mipLevel: 1, arrayLayer: 2, aspect: 'stencil-only' });
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('requires readback results to preserve resource and subresource provenance', () => {
|
|
103
|
+
const provenance: ReplayReadbackResult['provenance'] = {
|
|
104
|
+
generation: 1,
|
|
105
|
+
resourceId: 'texture:color',
|
|
106
|
+
subresource: {
|
|
107
|
+
mipLevel: 0,
|
|
108
|
+
arrayLayer: 0,
|
|
109
|
+
aspect: 'all',
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
expect(provenance.resourceId).toBe('texture:color');
|
|
113
|
+
expect(provenance.generation).toBeGreaterThanOrEqual(0);
|
|
114
|
+
expect(provenance.subresource).toEqual({
|
|
115
|
+
mipLevel: 0,
|
|
116
|
+
arrayLayer: 0,
|
|
117
|
+
aspect: 'all',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BootstrapResource, Tape } from '../protocol/types';
|
|
2
|
+
|
|
3
|
+
export const READBACK_MATRIX_CASES = [
|
|
4
|
+
{ format: 'rgba8unorm', dimension: '2d', aspect: 'all' },
|
|
5
|
+
{ format: 'depth32float', dimension: '2d', aspect: 'depth-only' },
|
|
6
|
+
{ format: 'depth24plus', dimension: '2d', aspect: 'depth-only' },
|
|
7
|
+
{ format: 'depth24plus-stencil8', dimension: '2d', aspect: 'stencil-only' },
|
|
8
|
+
{ format: 'rgba8unorm', dimension: '2d-array', aspect: 'all' },
|
|
9
|
+
{ format: 'rgba8unorm', dimension: 'cube', aspect: 'all' },
|
|
10
|
+
{ format: 'rgba8unorm', dimension: 'cube-array', aspect: 'all' },
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
13
|
+
export function makeReadbackMatrixTape(resources: readonly BootstrapResource[]): Tape {
|
|
14
|
+
return {
|
|
15
|
+
header: { formatVersion: 7, rhiCaps: {}, eventCount: 0, blobCount: 0 },
|
|
16
|
+
bootstrap: resources,
|
|
17
|
+
events: [],
|
|
18
|
+
blobs: [],
|
|
19
|
+
};
|
|
20
|
+
}
|