@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,324 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" />
|
|
2
|
+
// @forgeax/engine-rhi-debug/texel-layout -- SSOT for how a color texture's GPU
|
|
3
|
+
// bytes are laid out, shared by the snapshot readback (recorder) and the seed
|
|
4
|
+
// write-back (replayer) so both agree on byte layout without duplicating it in
|
|
5
|
+
// the tape (architecture-principles #1 SSOT / #2 Derive: format + size +
|
|
6
|
+
// mipLevelCount already live in the createTexture event; layout is derived, not
|
|
7
|
+
// re-stored in the initialData event).
|
|
8
|
+
//
|
|
9
|
+
// Why this exists: the original frame-header snapshot hardcoded bytesPerRow =
|
|
10
|
+
// width*4, depthOrArrayLayers = 1, mipLevel = 0 -- correct only for a single-mip
|
|
11
|
+
// single-layer 4-byte texture (rgba8). IBL resources break all three: the
|
|
12
|
+
// irradiance/prefilter cubemaps are rgba16float (8 B/texel), 6 array layers, and
|
|
13
|
+
// the prefilter map has a 5-mip roughness chain. Snapshotting them with the old
|
|
14
|
+
// assumptions either skipped them (-> replay renders unlit/black) or would have
|
|
15
|
+
// seeded corrupt bytes. This module computes the real per-subresource layout so a
|
|
16
|
+
// full snapshot + faithful seed round-trips each color format with a known texel
|
|
17
|
+
// block footprint, including block-compressed formats.
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Bytes per texel for uncompressed color formats. Block-compressed formats use
|
|
21
|
+
* {@link textureBlockLayout} because their bytes are addressed by blocks.
|
|
22
|
+
*/
|
|
23
|
+
export function bytesPerTexel(format: GPUTextureFormat | undefined): number | undefined {
|
|
24
|
+
if (format === undefined) return undefined;
|
|
25
|
+
return TEXEL_BYTES[format];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Uncompressed color formats only. Keyed to the W3C WebGPU GPUTextureFormat
|
|
29
|
+
// names. Depth/stencil and block-compressed formats are deliberately absent.
|
|
30
|
+
const TEXEL_BYTES: Partial<Record<GPUTextureFormat, number>> = {
|
|
31
|
+
// 8-bit channels
|
|
32
|
+
r8unorm: 1,
|
|
33
|
+
r8snorm: 1,
|
|
34
|
+
r8uint: 1,
|
|
35
|
+
r8sint: 1,
|
|
36
|
+
rg8unorm: 2,
|
|
37
|
+
rg8snorm: 2,
|
|
38
|
+
rg8uint: 2,
|
|
39
|
+
rg8sint: 2,
|
|
40
|
+
rgba8unorm: 4,
|
|
41
|
+
'rgba8unorm-srgb': 4,
|
|
42
|
+
rgba8snorm: 4,
|
|
43
|
+
rgba8uint: 4,
|
|
44
|
+
rgba8sint: 4,
|
|
45
|
+
bgra8unorm: 4,
|
|
46
|
+
'bgra8unorm-srgb': 4,
|
|
47
|
+
// 16-bit channels
|
|
48
|
+
r16uint: 2,
|
|
49
|
+
r16sint: 2,
|
|
50
|
+
r16float: 2,
|
|
51
|
+
rg16uint: 4,
|
|
52
|
+
rg16sint: 4,
|
|
53
|
+
rg16float: 4,
|
|
54
|
+
rgba16uint: 8,
|
|
55
|
+
rgba16sint: 8,
|
|
56
|
+
rgba16float: 8,
|
|
57
|
+
// 32-bit channels
|
|
58
|
+
r32uint: 4,
|
|
59
|
+
r32sint: 4,
|
|
60
|
+
r32float: 4,
|
|
61
|
+
rg32uint: 8,
|
|
62
|
+
rg32sint: 8,
|
|
63
|
+
rg32float: 8,
|
|
64
|
+
rgba32uint: 16,
|
|
65
|
+
rgba32sint: 16,
|
|
66
|
+
rgba32float: 16,
|
|
67
|
+
// packed
|
|
68
|
+
rgb10a2unorm: 4,
|
|
69
|
+
rg11b10ufloat: 4,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export interface TextureBlockLayout {
|
|
73
|
+
readonly blockWidth: number;
|
|
74
|
+
readonly blockHeight: number;
|
|
75
|
+
readonly bytesPerBlock: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const COMPRESSED_BLOCKS: Partial<Record<GPUTextureFormat, TextureBlockLayout>> = {
|
|
79
|
+
// BCn: all formats use 4x4 blocks; BC1/BC4 are 8 bytes, the rest 16.
|
|
80
|
+
'bc1-rgba-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
81
|
+
'bc1-rgba-unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
82
|
+
'bc2-rgba-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
83
|
+
'bc2-rgba-unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
84
|
+
'bc3-rgba-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
85
|
+
'bc3-rgba-unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
86
|
+
'bc4-r-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
87
|
+
'bc4-r-snorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
88
|
+
'bc5-rg-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
89
|
+
'bc5-rg-snorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
90
|
+
'bc6h-rgb-ufloat': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
91
|
+
'bc6h-rgb-float': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
92
|
+
'bc7-rgba-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
93
|
+
'bc7-rgba-unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
94
|
+
// ETC2/EAC: all formats use 4x4 blocks; one or two 64-bit blocks.
|
|
95
|
+
'etc2-rgb8unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
96
|
+
'etc2-rgb8unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
97
|
+
'etc2-rgb8a1unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
98
|
+
'etc2-rgb8a1unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
99
|
+
'etc2-rgba8unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
100
|
+
'etc2-rgba8unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
101
|
+
'eac-r11unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
102
|
+
'eac-r11snorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 8 },
|
|
103
|
+
'eac-rg11unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
104
|
+
'eac-rg11snorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
105
|
+
// ASTC uses 16-byte blocks with a format-specific footprint.
|
|
106
|
+
'astc-4x4-unorm': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
107
|
+
'astc-4x4-unorm-srgb': { blockWidth: 4, blockHeight: 4, bytesPerBlock: 16 },
|
|
108
|
+
'astc-5x4-unorm': { blockWidth: 5, blockHeight: 4, bytesPerBlock: 16 },
|
|
109
|
+
'astc-5x4-unorm-srgb': { blockWidth: 5, blockHeight: 4, bytesPerBlock: 16 },
|
|
110
|
+
'astc-5x5-unorm': { blockWidth: 5, blockHeight: 5, bytesPerBlock: 16 },
|
|
111
|
+
'astc-5x5-unorm-srgb': { blockWidth: 5, blockHeight: 5, bytesPerBlock: 16 },
|
|
112
|
+
'astc-6x5-unorm': { blockWidth: 6, blockHeight: 5, bytesPerBlock: 16 },
|
|
113
|
+
'astc-6x5-unorm-srgb': { blockWidth: 6, blockHeight: 5, bytesPerBlock: 16 },
|
|
114
|
+
'astc-6x6-unorm': { blockWidth: 6, blockHeight: 6, bytesPerBlock: 16 },
|
|
115
|
+
'astc-6x6-unorm-srgb': { blockWidth: 6, blockHeight: 6, bytesPerBlock: 16 },
|
|
116
|
+
'astc-8x5-unorm': { blockWidth: 8, blockHeight: 5, bytesPerBlock: 16 },
|
|
117
|
+
'astc-8x5-unorm-srgb': { blockWidth: 8, blockHeight: 5, bytesPerBlock: 16 },
|
|
118
|
+
'astc-8x6-unorm': { blockWidth: 8, blockHeight: 6, bytesPerBlock: 16 },
|
|
119
|
+
'astc-8x6-unorm-srgb': { blockWidth: 8, blockHeight: 6, bytesPerBlock: 16 },
|
|
120
|
+
'astc-8x8-unorm': { blockWidth: 8, blockHeight: 8, bytesPerBlock: 16 },
|
|
121
|
+
'astc-8x8-unorm-srgb': { blockWidth: 8, blockHeight: 8, bytesPerBlock: 16 },
|
|
122
|
+
'astc-10x5-unorm': { blockWidth: 10, blockHeight: 5, bytesPerBlock: 16 },
|
|
123
|
+
'astc-10x5-unorm-srgb': { blockWidth: 10, blockHeight: 5, bytesPerBlock: 16 },
|
|
124
|
+
'astc-10x6-unorm': { blockWidth: 10, blockHeight: 6, bytesPerBlock: 16 },
|
|
125
|
+
'astc-10x6-unorm-srgb': { blockWidth: 10, blockHeight: 6, bytesPerBlock: 16 },
|
|
126
|
+
'astc-10x8-unorm': { blockWidth: 10, blockHeight: 8, bytesPerBlock: 16 },
|
|
127
|
+
'astc-10x8-unorm-srgb': { blockWidth: 10, blockHeight: 8, bytesPerBlock: 16 },
|
|
128
|
+
'astc-10x10-unorm': { blockWidth: 10, blockHeight: 10, bytesPerBlock: 16 },
|
|
129
|
+
'astc-10x10-unorm-srgb': { blockWidth: 10, blockHeight: 10, bytesPerBlock: 16 },
|
|
130
|
+
'astc-12x10-unorm': { blockWidth: 12, blockHeight: 10, bytesPerBlock: 16 },
|
|
131
|
+
'astc-12x10-unorm-srgb': { blockWidth: 12, blockHeight: 10, bytesPerBlock: 16 },
|
|
132
|
+
'astc-12x12-unorm': { blockWidth: 12, blockHeight: 12, bytesPerBlock: 16 },
|
|
133
|
+
'astc-12x12-unorm-srgb': { blockWidth: 12, blockHeight: 12, bytesPerBlock: 16 },
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/** Return the WebGPU texel-block footprint for each supported color format. */
|
|
137
|
+
export function textureBlockLayout(
|
|
138
|
+
format: GPUTextureFormat | undefined,
|
|
139
|
+
): TextureBlockLayout | undefined {
|
|
140
|
+
if (format === undefined) return undefined;
|
|
141
|
+
return (
|
|
142
|
+
COMPRESSED_BLOCKS[format] ??
|
|
143
|
+
(TEXEL_BYTES[format] === undefined
|
|
144
|
+
? undefined
|
|
145
|
+
: { blockWidth: 1, blockHeight: 1, bytesPerBlock: TEXEL_BYTES[format] })
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// formatInfo -- per-channel semantics for CPU-side decode (viewer preview)
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/** How a channel's bits are interpreted (drives the host-side decode-to-RGBA8). */
|
|
154
|
+
export type ChannelType = 'unorm' | 'snorm' | 'uint' | 'sint' | 'float' | 'ufloat';
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Per-channel layout of an uncompressed color format, enough to decode its raw
|
|
158
|
+
* GPU bytes into displayable RGBA8 on the host (no GPU). Sits beside
|
|
159
|
+
* {@link bytesPerTexel} (which stays the SSOT for total texel size); the
|
|
160
|
+
* per-channel byte width is derived as `bytesPerTexel / channels` for plain
|
|
161
|
+
* formats, so no byte count is re-stored here. `packed` formats pack all
|
|
162
|
+
* channels into a single 4-byte word (channels read by bit-field, not by a
|
|
163
|
+
* uniform per-channel width).
|
|
164
|
+
*/
|
|
165
|
+
export interface FormatInfo {
|
|
166
|
+
readonly channels: 1 | 2 | 3 | 4;
|
|
167
|
+
readonly channelType: ChannelType;
|
|
168
|
+
/** Channels stored blue-first in memory (swizzle B<->R when decoding). */
|
|
169
|
+
readonly bgra?: boolean;
|
|
170
|
+
/** Bit-packed layout (channels share one 32-bit word) needing special unpack. */
|
|
171
|
+
readonly packed?: 'rgb10a2unorm' | 'rg11b10ufloat';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Keyed to the same uncompressed color formats as TEXEL_BYTES. Depth/stencil and
|
|
175
|
+
// block-compressed formats are absent (return undefined -> caller falls back).
|
|
176
|
+
const FORMAT_INFO: Partial<Record<GPUTextureFormat, FormatInfo>> = {
|
|
177
|
+
// 8-bit channels
|
|
178
|
+
r8unorm: { channels: 1, channelType: 'unorm' },
|
|
179
|
+
r8snorm: { channels: 1, channelType: 'snorm' },
|
|
180
|
+
r8uint: { channels: 1, channelType: 'uint' },
|
|
181
|
+
r8sint: { channels: 1, channelType: 'sint' },
|
|
182
|
+
rg8unorm: { channels: 2, channelType: 'unorm' },
|
|
183
|
+
rg8snorm: { channels: 2, channelType: 'snorm' },
|
|
184
|
+
rg8uint: { channels: 2, channelType: 'uint' },
|
|
185
|
+
rg8sint: { channels: 2, channelType: 'sint' },
|
|
186
|
+
rgba8unorm: { channels: 4, channelType: 'unorm' },
|
|
187
|
+
'rgba8unorm-srgb': { channels: 4, channelType: 'unorm' },
|
|
188
|
+
rgba8snorm: { channels: 4, channelType: 'snorm' },
|
|
189
|
+
rgba8uint: { channels: 4, channelType: 'uint' },
|
|
190
|
+
rgba8sint: { channels: 4, channelType: 'sint' },
|
|
191
|
+
bgra8unorm: { channels: 4, channelType: 'unorm', bgra: true },
|
|
192
|
+
'bgra8unorm-srgb': { channels: 4, channelType: 'unorm', bgra: true },
|
|
193
|
+
// 16-bit channels
|
|
194
|
+
r16uint: { channels: 1, channelType: 'uint' },
|
|
195
|
+
r16sint: { channels: 1, channelType: 'sint' },
|
|
196
|
+
r16float: { channels: 1, channelType: 'float' },
|
|
197
|
+
rg16uint: { channels: 2, channelType: 'uint' },
|
|
198
|
+
rg16sint: { channels: 2, channelType: 'sint' },
|
|
199
|
+
rg16float: { channels: 2, channelType: 'float' },
|
|
200
|
+
rgba16uint: { channels: 4, channelType: 'uint' },
|
|
201
|
+
rgba16sint: { channels: 4, channelType: 'sint' },
|
|
202
|
+
rgba16float: { channels: 4, channelType: 'float' },
|
|
203
|
+
// 32-bit channels
|
|
204
|
+
r32uint: { channels: 1, channelType: 'uint' },
|
|
205
|
+
r32sint: { channels: 1, channelType: 'sint' },
|
|
206
|
+
r32float: { channels: 1, channelType: 'float' },
|
|
207
|
+
rg32uint: { channels: 2, channelType: 'uint' },
|
|
208
|
+
rg32sint: { channels: 2, channelType: 'sint' },
|
|
209
|
+
rg32float: { channels: 2, channelType: 'float' },
|
|
210
|
+
rgba32uint: { channels: 4, channelType: 'uint' },
|
|
211
|
+
rgba32sint: { channels: 4, channelType: 'sint' },
|
|
212
|
+
rgba32float: { channels: 4, channelType: 'float' },
|
|
213
|
+
// packed (channels share one 32-bit word; decoded by bit-field)
|
|
214
|
+
rgb10a2unorm: { channels: 4, channelType: 'unorm', packed: 'rgb10a2unorm' },
|
|
215
|
+
rg11b10ufloat: { channels: 3, channelType: 'ufloat', packed: 'rg11b10ufloat' },
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Per-channel layout of an uncompressed color format for host-side decode.
|
|
220
|
+
* Returns `undefined` for depth/stencil, block-compressed, or unknown formats
|
|
221
|
+
* (the viewer's preview path treats `undefined` as "not directly previewable").
|
|
222
|
+
*/
|
|
223
|
+
export function formatInfo(format: GPUTextureFormat | string | undefined): FormatInfo | undefined {
|
|
224
|
+
if (format === undefined) return undefined;
|
|
225
|
+
return FORMAT_INFO[format as GPUTextureFormat];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** One mip level of one array layer: its extent + where its tight bytes live in the blob. */
|
|
229
|
+
export interface SubresourceSlice {
|
|
230
|
+
readonly layer: number;
|
|
231
|
+
readonly mip: number;
|
|
232
|
+
readonly width: number;
|
|
233
|
+
readonly height: number;
|
|
234
|
+
readonly byteOffset: number;
|
|
235
|
+
readonly byteLength: number;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Full subresource layout of a texture snapshot blob. */
|
|
239
|
+
export interface TextureLayout {
|
|
240
|
+
readonly blockWidth: number;
|
|
241
|
+
readonly blockHeight: number;
|
|
242
|
+
readonly bytesPerBlock: number;
|
|
243
|
+
readonly layerCount: number;
|
|
244
|
+
readonly mipLevelCount: number;
|
|
245
|
+
readonly slices: readonly SubresourceSlice[];
|
|
246
|
+
/** Total tight byte length across every subresource (the blob size). */
|
|
247
|
+
readonly totalBytes: number;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface TextureExtent {
|
|
251
|
+
readonly width: number;
|
|
252
|
+
readonly height: number;
|
|
253
|
+
readonly layerCount: number;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Project a serialized GPU texture extent onto the dimensions used by snapshots. */
|
|
257
|
+
export function projectTextureExtent(size: unknown): TextureExtent {
|
|
258
|
+
if (typeof size === 'number') return { width: size, height: 1, layerCount: 1 };
|
|
259
|
+
if (Array.isArray(size)) {
|
|
260
|
+
const width = typeof size[0] === 'number' ? size[0] : 1;
|
|
261
|
+
const height = typeof size[1] === 'number' ? size[1] : width;
|
|
262
|
+
const layerCount = typeof size[2] === 'number' ? size[2] : 1;
|
|
263
|
+
return { width, height, layerCount };
|
|
264
|
+
}
|
|
265
|
+
if (size !== null && typeof size === 'object') {
|
|
266
|
+
const record = size as {
|
|
267
|
+
readonly width?: unknown;
|
|
268
|
+
readonly height?: unknown;
|
|
269
|
+
readonly depthOrArrayLayers?: unknown;
|
|
270
|
+
};
|
|
271
|
+
const width = typeof record.width === 'number' ? record.width : 1;
|
|
272
|
+
const height = typeof record.height === 'number' ? record.height : width;
|
|
273
|
+
const layerCount =
|
|
274
|
+
typeof record.depthOrArrayLayers === 'number' ? record.depthOrArrayLayers : 1;
|
|
275
|
+
return { width, height, layerCount };
|
|
276
|
+
}
|
|
277
|
+
return { width: 1, height: 1, layerCount: 1 };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Compute the canonical tight byte layout for a texture snapshot: every array
|
|
282
|
+
* layer, every mip level, packed in layer-major then mip-minor order with no row
|
|
283
|
+
* padding (tight bytesPerRow = blockCountX * bytesPerBlock). Mip dimensions halve
|
|
284
|
+
* per level (floor, min 1), matching the WebGPU mip size rule.
|
|
285
|
+
*
|
|
286
|
+
* Returns `undefined` when the format has no known block footprint. The
|
|
287
|
+
* recorder reads each slice into this exact offset; the replayer reads each
|
|
288
|
+
* slice back out and writeTexture's it to (layer, mip).
|
|
289
|
+
*/
|
|
290
|
+
export function computeTextureLayout(
|
|
291
|
+
format: GPUTextureFormat | undefined,
|
|
292
|
+
width: number,
|
|
293
|
+
height: number,
|
|
294
|
+
layerCount: number,
|
|
295
|
+
mipLevelCount: number,
|
|
296
|
+
): TextureLayout | undefined {
|
|
297
|
+
const block = textureBlockLayout(format);
|
|
298
|
+
if (block === undefined) return undefined;
|
|
299
|
+
|
|
300
|
+
const layers = Math.max(1, layerCount);
|
|
301
|
+
const mips = Math.max(1, mipLevelCount);
|
|
302
|
+
const slices: SubresourceSlice[] = [];
|
|
303
|
+
let offset = 0;
|
|
304
|
+
for (let layer = 0; layer < layers; layer++) {
|
|
305
|
+
for (let mip = 0; mip < mips; mip++) {
|
|
306
|
+
const mw = Math.max(1, width >> mip);
|
|
307
|
+
const mh = Math.max(1, height >> mip);
|
|
308
|
+
const blockCountX = Math.ceil(mw / block.blockWidth);
|
|
309
|
+
const blockCountY = Math.ceil(mh / block.blockHeight);
|
|
310
|
+
const byteLength = blockCountX * blockCountY * block.bytesPerBlock;
|
|
311
|
+
slices.push({ layer, mip, width: mw, height: mh, byteOffset: offset, byteLength });
|
|
312
|
+
offset += byteLength;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
blockWidth: block.blockWidth,
|
|
317
|
+
blockHeight: block.blockHeight,
|
|
318
|
+
bytesPerBlock: block.bytesPerBlock,
|
|
319
|
+
layerCount: layers,
|
|
320
|
+
mipLevelCount: mips,
|
|
321
|
+
slices,
|
|
322
|
+
totalBytes: offset,
|
|
323
|
+
};
|
|
324
|
+
}
|