@carbonenginejs/runtime-resource 0.11.1 → 0.12.1
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/dist/CjsResMan.js +4 -4
- package/dist/CjsResMan.js.map +1 -1
- package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -4
- package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/dist/format/CjsByteReader.js +310 -0
- package/dist/format/CjsByteReader.js.map +1 -0
- package/dist/format/CjsByteWriter.js +242 -0
- package/dist/format/CjsByteWriter.js.map +1 -0
- package/dist/format/CjsFormatError.js +41 -0
- package/dist/format/CjsFormatError.js.map +1 -0
- package/dist/format/CjsStringTable.js +268 -0
- package/dist/format/CjsStringTable.js.map +1 -0
- package/dist/format/carbonEffect/CjsCarbonEffectReader.js +361 -0
- package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -0
- package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -0
- package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -0
- package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +372 -0
- package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +1 -0
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js +427 -0
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -0
- package/dist/format/carbonEffect/carbonEffectRecords.js +955 -0
- package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -0
- package/dist/format/compareUtf8.js +36 -0
- package/dist/format/compareUtf8.js.map +1 -0
- package/dist/format/index.js +11 -0
- package/dist/format/index.js.map +1 -0
- package/dist/formats/bnk/CjsBnkFormat.js +18 -2
- package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
- package/dist/formats/bnk/core/eventAction.js +305 -0
- package/dist/formats/bnk/core/eventAction.js.map +1 -0
- package/dist/formats/bnk/core/graph.js +3 -6
- package/dist/formats/bnk/core/graph.js.map +1 -1
- package/dist/formats/bnk/core/helpers.js +62 -11
- package/dist/formats/bnk/core/helpers.js.map +1 -1
- package/dist/formats/bnk/core/nodeBase.js +532 -0
- package/dist/formats/bnk/core/nodeBase.js.map +1 -0
- package/dist/formats/bnk/core/sfxNodes.js +632 -0
- package/dist/formats/bnk/core/sfxNodes.js.map +1 -0
- package/dist/formats/bnk/core/soundbanksInfo.js +59 -51
- package/dist/formats/bnk/core/soundbanksInfo.js.map +1 -1
- package/dist/formats/gr2/CjsGr2Format.js +256 -13
- package/dist/formats/gr2/CjsGr2Format.js.map +1 -1
- package/dist/formats/gr2/core/helpers.js +15 -15
- package/dist/formats/gr2/core/helpers.js.map +1 -1
- package/dist/formats/gr2/core/json.js +1 -1
- package/dist/formats/gr2/core/json.js.map +1 -1
- package/dist/formats/gr2/core/targets.js +1 -1
- package/dist/formats/gr2/core/targets.js.map +1 -1
- package/dist/formats/gr2/index.js +0 -1
- package/dist/formats/gr2/index.js.map +1 -1
- package/dist/formats/hlsl/core/HlslReader.js +7 -257
- package/dist/formats/hlsl/core/HlslReader.js.map +1 -1
- package/dist/formats/hlsl/core/analysis.js +1 -1
- package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +781 -0
- package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +21 -1
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
- package/dist/formats/hlsl/index.js +1 -0
- package/dist/formats/hlsl/index.js.map +1 -1
- package/dist/formats/index.js +3 -6
- package/dist/formats/index.js.map +1 -1
- package/dist/formats/webgl/core/cewg/CewgPackage.js +2 -1
- package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -1
- package/dist/formats/webgl/core/cewg/binary.js +11 -93
- package/dist/formats/webgl/core/cewg/binary.js.map +1 -1
- package/dist/formats/webgl/core/effectPackage.js +4 -1
- package/dist/formats/webgl/core/effectPackage.js.map +1 -1
- package/dist/formats/webgl/core/errors.js +3 -3
- package/dist/formats/webgl/core/errors.js.map +1 -1
- package/dist/formats/webgpu/CjsWebgpuFormat.js +4 -23
- package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
- package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +197 -0
- package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js +368 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/containerViews.js +355 -0
- package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/validateContainer.js +90 -0
- package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +1 -0
- package/dist/formats/webgpu/core/effectBackendBodySet.js +21 -20
- package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
- package/dist/formats/webgpu/core/helpers.js +87 -92
- package/dist/formats/webgpu/core/helpers.js.map +1 -1
- package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +14 -13
- package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -1
- package/dist/formats/webgpu/core/ir/inferValueTypes.js +10 -8
- package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -1
- package/dist/formats/webgpu/core/packageEffect.js +94 -9
- package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
- package/dist/formats/webgpu/core/packageMetadata.js +10 -1
- package/dist/formats/webgpu/core/packageMetadata.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +17 -15
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +13 -12
- package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +9 -8
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +23 -22
- package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -1
- package/dist/formats/webgpu/core/wgsl/selectionPlans.js +46 -44
- package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -1
- package/dist/resource/CjsResource.js +700 -684
- package/dist/resource/CjsResource.js.map +1 -1
- package/dist/resource/Tr2LightProfileRes.js +18 -27
- package/dist/resource/Tr2LightProfileRes.js.map +1 -1
- package/dist/resource/audio/CjsAudioBufferRes.js +2 -2
- package/dist/resource/audio/CjsAudioBufferRes.js.map +1 -1
- package/dist/resource/audio/CjsAudioRes.js +2 -2
- package/dist/resource/audio/CjsAudioRes.js.map +1 -1
- package/dist/resource/geometry/TriGeometryRes.js +605 -586
- package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
- package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +21 -30
- package/dist/resource/geometry/granny/Tr2GrannyStateRes.js.map +1 -1
- package/dist/resource/geometry/granny/TriGrannyRes.js +21 -30
- package/dist/resource/geometry/granny/TriGrannyRes.js.map +1 -1
- package/dist/resource/shader/Tr2EffectRes.js +9 -7
- package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialArea.js +5 -3
- package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialMesh.js +4 -2
- package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialRes.js +5 -3
- package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
- package/dist/resource/shader/Tr2Shader.js +26 -21
- package/dist/resource/shader/Tr2Shader.js.map +1 -1
- package/dist/resource/shader/Tr2ShaderPermutation.js +8 -6
- package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectDefine.js +5 -3
- package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectDescription.js +5 -3
- package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js +17 -15
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +11 -9
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectResource.js +8 -6
- package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js +15 -13
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js +7 -5
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2Pass.js +11 -9
- package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js +8 -6
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
- package/dist/resource/texture/CjsTextureArrayRes.js +400 -411
- package/dist/resource/texture/CjsTextureArrayRes.js.map +1 -1
- package/dist/resource/texture/Tr2ImageRes.js +99 -95
- package/dist/resource/texture/Tr2ImageRes.js.map +1 -1
- package/dist/resource/texture/TriTextureRes.js +312 -273
- package/dist/resource/texture/TriTextureRes.js.map +1 -1
- package/docs/README.md +13 -1
- package/docs/architecture.md +3 -3
- package/docs/concepts/resource-lifecycle.md +9 -2
- package/docs/concepts/shader-resource-model.md +114 -0
- package/docs/concepts/writing-an-engine-adapter.md +115 -0
- package/docs/formats/README.md +24 -1
- package/docs/formats/carbon-effect-container.md +452 -0
- package/docs/formats/dxbc/README.md +68 -0
- package/docs/formats/dxbc/architecture.md +80 -0
- package/docs/formats/dxbc/reference/api.md +77 -0
- package/docs/formats/dxbc/reference/classes/README.md +9 -0
- package/docs/formats/dxbc/reference/decoded-output.md +122 -0
- package/docs/formats/gr2.md +3 -4
- package/docs/formats/hlsl/README.md +54 -0
- package/docs/formats/hlsl/architecture.md +67 -0
- package/docs/formats/hlsl/guides/hydrating-json-output.md +62 -0
- package/docs/formats/hlsl/guides/reading-effects.md +64 -0
- package/docs/formats/hlsl/reference/advanced-analysis.md +66 -0
- package/docs/formats/hlsl/reference/api.md +98 -0
- package/docs/formats/hlsl/reference/classes/README.md +11 -0
- package/docs/formats/hlsl/reference/json-graph.md +100 -0
- package/docs/formats/hlsl/reference/portable-reflection.md +141 -0
- package/docs/formats/provenance.md +32 -17
- package/docs/formats/webgl/README.md +57 -0
- package/docs/formats/webgl/architecture.md +70 -0
- package/docs/formats/webgl/carbon-constant-layouts.md +326 -0
- package/docs/formats/webgl/decl-io.md +1234 -0
- package/docs/formats/webgl/effect-reflection.md +127 -0
- package/docs/formats/webgl/memory-structured.md +871 -0
- package/docs/formats/webgl/reference/classes/README.md +9 -0
- package/docs/formats/webgl/texture-sample.md +964 -0
- package/docs/formats/webgpu/README.md +84 -0
- package/docs/formats/webgpu/architecture.md +96 -0
- package/docs/formats/webgpu/formats/cewgpu.md +216 -0
- package/docs/formats/webgpu/guides/effect-packaging.md +191 -0
- package/docs/formats/webgpu/reference/api.md +197 -0
- package/docs/formats/webgpu/reference/classes/README.md +9 -0
- package/docs/formats/webgpu/reference/wgsl-compatibility.md +1543 -0
- package/docs/formats/wwise.md +45 -4
- package/docs/reference/classes/core.md +80 -0
- package/docs/reference/classes/formats.md +24 -34
- package/docs/reference/events.md +25 -0
- package/docs/reference/motherlode-cache.md +22 -8
- package/docs/reference/workers.md +5 -5
- package/docs/roadmap.md +61 -41
- package/format-notices/bnk/NOTICE +5 -4
- package/format-notices/webgl/NOTICE +1 -1
- package/package.json +2 -1
- package/dist/formats/gr2/core/CjsFormatGr2.js +0 -273
- package/dist/formats/gr2/core/CjsFormatGr2.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +0 -415
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +0 -100
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/binary.js +0 -93
- package/dist/formats/webgpu/core/cewgpu/binary.js.map +0 -1
- package/dist/formats/webgpu/core/cewgpu/tags.js +0 -17
- package/dist/formats/webgpu/core/cewgpu/tags.js.map +0 -1
- package/dist/formats/webgpu/core/effectPackageValidation.js +0 -1078
- package/dist/formats/webgpu/core/effectPackageValidation.js.map +0 -1
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
# Carbon compiled-effect container
|
|
2
|
+
|
|
3
|
+
Status: Stable
|
|
4
|
+
Visibility: Public
|
|
5
|
+
Scope: `@carbonenginejs/runtime-resource` — `src/format/carbonEffect/`, `src/format/CjsByteReader.js`, `src/format/CjsByteWriter.js`, `src/format/CjsStringTable.js`
|
|
6
|
+
Audience: Anyone reading or writing compiled shader effect bytes, or extending `.cewgpu`
|
|
7
|
+
Summary: The v15 binary layout of Carbon's compiled effect files, the shared byte reader and writer that implement it, and the arena offset policy a byte-exact re-emit depends on.
|
|
8
|
+
|
|
9
|
+
## What this is
|
|
10
|
+
|
|
11
|
+
Carbon's shader compiler emits one file per effect containing **every permutation**,
|
|
12
|
+
selected at read time through an offset table. The format has three parts: a header,
|
|
13
|
+
a deduplicated blob arena ("string table"), one dense offset-table row per
|
|
14
|
+
permutation, and one stored description blob per distinct encoded body.
|
|
15
|
+
|
|
16
|
+
This package implements it as a shared reader and writer, verified byte-exact against
|
|
17
|
+
CCP's own shipped files. It is the wire foundation for `.cewgpu`. The `.cewg`
|
|
18
|
+
WebGL package retains its separate `CEWG` magic and chunk layout.
|
|
19
|
+
|
|
20
|
+
| module | role |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `src/format/CjsByteReader.js` | little-endian cursor plus arena resolution primitives; shared by the HLSL, Carbon-effect, and WebGL readers |
|
|
23
|
+
| `src/format/CjsByteWriter.js` | growable append cursor with reserve-and-patch |
|
|
24
|
+
| `src/format/CjsStringTable.js` | the blob arena, with Carbon's bytewise-sorted offset assignment |
|
|
25
|
+
| `src/format/carbonEffect/carbonEffectRecords.js` | the v15 description-blob record codec |
|
|
26
|
+
| `src/format/carbonEffect/CjsCarbonEffectReader.js` | container reader and structural checks |
|
|
27
|
+
| `src/format/carbonEffect/CjsCarbonEffectWriter.js` | container writer, offset arithmetic and alias dedupe |
|
|
28
|
+
|
|
29
|
+
## Why v15 only
|
|
30
|
+
|
|
31
|
+
The reader and writer accept and emit version 15 and nothing else.
|
|
32
|
+
|
|
33
|
+
Carbon's own reader accepts versions 2 through 15, but its v13/v14 branches mark
|
|
34
|
+
the field-order boundaries as uncertain. Version 15 is the version with an
|
|
35
|
+
authoritative writer to check against, and the entire audited shipped corpus at
|
|
36
|
+
build 3444265 is v15 — 3222 files across `effect.dx11` and `effect.dx12`, plus
|
|
37
|
+
the same 537 shaders again under `effect.metal`. Nothing older appears in that
|
|
38
|
+
audited corpus.
|
|
39
|
+
|
|
40
|
+
The v15 body is byte-identical to v14. Version 15 differs from 14 only by the 36
|
|
41
|
+
extra header bytes: the compiler version and the source hash.
|
|
42
|
+
|
|
43
|
+
## Layout
|
|
44
|
+
|
|
45
|
+
### Header
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
u32 version = 15
|
|
49
|
+
u8[4] shaderCompilerVersion {major, minor, patch, tweak}
|
|
50
|
+
char[32] sourceHash ASCII hex MD5 of the HLSL source inputs
|
|
51
|
+
u32 stringTableSize | arena payload
|
|
52
|
+
u8 permutationCount | permutation records
|
|
53
|
+
u32 recordCount | recordCount x { u32 index, u32 offset, u32 size }
|
|
54
|
+
description blobs
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The compiler writes this order and the runtime reads the same order.
|
|
58
|
+
|
|
59
|
+
The compiler version is **four bytes, not a `u32`**, and Carbon's rebuild check
|
|
60
|
+
compares only the first three. A shipped v15 header reads `01 02 06 00` —
|
|
61
|
+
compiler 1.2.6.0, matching the ShaderCompiler project version. As a `u32` those
|
|
62
|
+
bytes are `0x00060201`, which means nothing. `HlslEffectRes` historically read
|
|
63
|
+
the field as a dword; it now also exposes `m_compilerVersionBytes`, which is the
|
|
64
|
+
truthful reading and what new code should use. The dword form survives only
|
|
65
|
+
because it is republished as `source.compilerVersion` in the portable
|
|
66
|
+
reflection, where it is asserted to be an unsigned integer and covered by a
|
|
67
|
+
package digest.
|
|
68
|
+
|
|
69
|
+
The compiler's rebuild check reads the 32-byte hash, while the runtime skips it.
|
|
70
|
+
It is provenance, not integrity.
|
|
71
|
+
|
|
72
|
+
A permutation record is:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
u32 nameOffset | u8 defaultOption | u32 descriptionOffset | u8 type | u8 optionCount | u32 optionOffset[optionCount]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Note the field order: `defaultOption` sits between the name and the description.
|
|
79
|
+
Carbon writes that byte inside a conditional loop with no `else`, so a
|
|
80
|
+
permutation whose declared default matches no option would emit a record one
|
|
81
|
+
byte short and desynchronise the entire rest of the header. Our writer always
|
|
82
|
+
emits it.
|
|
83
|
+
|
|
84
|
+
### Body-offset arithmetic
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
base = 4 + 4 + 32 + headerSize + stringTable.GetSize()
|
|
88
|
+
headerSize = (recordCount * 3 + 1) * 4 + permutationBytes
|
|
89
|
+
permutationBytes = 1 + Σ (11 + optionCount * 4)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`GetSize()` **includes** the arena's own `u32` length prefix, so the prefix is
|
|
93
|
+
counted exactly once. Row offsets are absolute from byte 0 of the file.
|
|
94
|
+
`CjsCarbonEffectWriter` asserts that the bytes it actually wrote before the
|
|
95
|
+
first body equal this computed base, so an arithmetic error fails loudly rather
|
|
96
|
+
than shifting every body.
|
|
97
|
+
|
|
98
|
+
### The arena
|
|
99
|
+
|
|
100
|
+
`CjsStringTable` is Carbon's `StringTable`. Three properties matter:
|
|
101
|
+
|
|
102
|
+
1. **Offsets are assigned by a bytewise sort, not by insertion order.**
|
|
103
|
+
The comparison is `memcmp` over the shorter length, then shorter-wins on a
|
|
104
|
+
tie, before cumulative offsets are assigned. Any writer that assigns
|
|
105
|
+
first-seen offsets produces a valid file that is not byte-identical to
|
|
106
|
+
Carbon's.
|
|
107
|
+
2. **Dedupe is on exact bytes with no suffix merging.** `"red"` gets its own
|
|
108
|
+
entry even though it is a suffix of `"shared"`.
|
|
109
|
+
3. **There are two kinds of entry and no manifest.** A NUL-terminated string is
|
|
110
|
+
added with its terminator and referenced by a bare `u32` offset. A sized blob
|
|
111
|
+
— shader bytecode, program source, default constant values — is added with
|
|
112
|
+
exactly its own bytes and referenced by a `{u32 size, u32 offset}` pair. The
|
|
113
|
+
arena writes a `u32` payload size and the payload, nothing else; every
|
|
114
|
+
reference site resolves its own entry.
|
|
115
|
+
|
|
116
|
+
## Two rules for anything added later
|
|
117
|
+
|
|
118
|
+
Neither is visible from Carbon's own code, and both were found by implementing
|
|
119
|
+
rather than by reading. They constrain every future addition to this format.
|
|
120
|
+
|
|
121
|
+
**Rule 1: every sized record must parse to exactly its declared end.** Trailing
|
|
122
|
+
bytes mean one of two things and both are fatal — the writer knew fields this reader
|
|
123
|
+
does not, or the writer miscounted. Enforced for the description blob
|
|
124
|
+
(`readEffectDescription`), for the per-pass backend block (`readBackendBlock`), and
|
|
125
|
+
for the header, whose end must equal where the body region begins.
|
|
126
|
+
|
|
127
|
+
This rule carries weight that used to live elsewhere. The chunk container it replaces
|
|
128
|
+
spent roughly 600 lines asserting that its several projections of one effect still
|
|
129
|
+
agreed with each other, and those checks caught a malformed *tree* — our writer
|
|
130
|
+
emitting something structurally wrong — not only a malformed file. A record layout
|
|
131
|
+
makes most of that question unaskable, because containment replaces reference and
|
|
132
|
+
position replaces key. What remains is this: a writer bug either fails to parse,
|
|
133
|
+
which announces itself, or it parses and leaves the cursor somewhere other than the
|
|
134
|
+
declared end. Applying the rule to some sized records and not others is a gap that
|
|
135
|
+
stays invisible until a writer bug hides in one of the others.
|
|
136
|
+
|
|
137
|
+
**Rule 2: anything placed in the arena must be arena-independent.** An arena entry
|
|
138
|
+
cannot contain an arena offset. Offsets are assigned by the content sort, the sort
|
|
139
|
+
depends on every entry's bytes, so an entry that referred to the arena would have to
|
|
140
|
+
be interned before its own contents could be computed — a circular dependency with
|
|
141
|
+
no fixed point. This is invisible in Carbon's own code because no Carbon arena blob
|
|
142
|
+
refers to the arena: strings, bytecode and default constant values are all leaves.
|
|
143
|
+
Our per-pass backend block is the first non-leaf candidate, and it is why that block
|
|
144
|
+
carries inline length-prefixed strings instead of references. A test pins the
|
|
145
|
+
property directly — the block's bytes must be identical whichever arena it is
|
|
146
|
+
interned into. Any future arena entry must satisfy the same rule.
|
|
147
|
+
|
|
148
|
+
### One field the container cannot round-trip
|
|
149
|
+
|
|
150
|
+
For a **non-dynamic sampler, the name is not preserved.** The file stores one, but
|
|
151
|
+
Carbon's reader nulls it before any producer sees it, so a package built from
|
|
152
|
+
our reflection carries the empty string.
|
|
153
|
+
|
|
154
|
+
This is a property of the input, not a bug in the mapping: the name is unrecoverable
|
|
155
|
+
by the time we receive the data, rather than dropped on the way out. Carbon nulls it
|
|
156
|
+
precisely because a non-dynamic sampler is never looked up by name — `FindSamplerByName`
|
|
157
|
+
only matters for the dynamic case. Recorded here because it will otherwise be
|
|
158
|
+
rediscovered as a bug: a diff against the source effect will always show it.
|
|
159
|
+
|
|
160
|
+
**Corollary: the container admits all six of Carbon's stage types.** `stages` is
|
|
161
|
+
capped at `SHADER_TYPE_COUNT` = 6, and the stage-type byte uses Carbon's
|
|
162
|
+
`InputStageType` numbering: vertex, pixel, compute, geometry, hull, domain. A
|
|
163
|
+
backend that can only express three of those rejects the rest in its own layer;
|
|
164
|
+
the container does not narrow on its behalf. The Carbon region is
|
|
165
|
+
backend-invariant, and restrictions belong to the backend.
|
|
166
|
+
|
|
167
|
+
`0xffffffff` is the null reference. It is legal at **exactly one wire
|
|
168
|
+
position**: a stage's default-constant-value offset when the accompanying size
|
|
169
|
+
is zero, which the optional-value reader consumes without dereferencing.
|
|
170
|
+
Everywhere else a `0xffffffff` offset fails the load.
|
|
171
|
+
|
|
172
|
+
Two deliberate departures from Carbon, both of which make byte-identical output
|
|
173
|
+
more likely rather than less:
|
|
174
|
+
|
|
175
|
+
- **`m_size` is initialised.** Carbon's constructor leaves it indeterminate and
|
|
176
|
+
gets away with it only because the one instance is a zero-initialised global.
|
|
177
|
+
- **Adding after an offset has been handed out is an error.** In Carbon,
|
|
178
|
+
`GetOffset` re-sorts a dirty table, which reassigns *every* offset — including
|
|
179
|
+
offsets already baked into packed bodies. Carbon avoids the corruption by
|
|
180
|
+
interning all late strings before the packing pass. `CjsCarbonEffectWriter`
|
|
181
|
+
reproduces that discipline structurally: it runs the record walk twice, once
|
|
182
|
+
with `collectArena` to intern and once with `internArena` to emit. Because both
|
|
183
|
+
passes drive the same `writeEffectDescription`, they cannot drift apart.
|
|
184
|
+
|
|
185
|
+
### Description blob, v15 field order
|
|
186
|
+
|
|
187
|
+
Derived independently from the writer's save order and the reader's load order,
|
|
188
|
+
then confirmed to agree field for field. Counts are `u8` unless marked.
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
u8 techniqueCount
|
|
192
|
+
u32 name
|
|
193
|
+
u8 passCount cap 64
|
|
194
|
+
u8 stageCount cap 6 (SHADER_TYPE_COUNT)
|
|
195
|
+
u8 stageType
|
|
196
|
+
u32 shaderSize | u32 shaderDataOffset program payload, arena blob
|
|
197
|
+
u32 threadGroupSize[3]
|
|
198
|
+
u8 pipelineInputCount cap 64
|
|
199
|
+
u8 usage, registerIndex, usageIndex, usedMask, type, dimension
|
|
200
|
+
-- StageData --
|
|
201
|
+
u8 registerCount
|
|
202
|
+
u8 registerType | u32 registerIndex | u32 registerCount | u8 registerSpace
|
|
203
|
+
u8 staticSamplerCount
|
|
204
|
+
u32 registerIndex | u8 registerSpace | u8 x7 filters/address
|
|
205
|
+
f32 mipLODBias | u8 maxAnisotropy | u8 comparisonFunc
|
|
206
|
+
u8 borderColor enum, NOT four floats
|
|
207
|
+
f32 minLOD | f32 maxLOD
|
|
208
|
+
u32 constantCount u32, not u8
|
|
209
|
+
u32 name | u32 offset | u32 size | u8 type | u8 dimension
|
|
210
|
+
u32 elements | u8 isSRGB | u8 isAutoregister
|
|
211
|
+
u32 defaultValuesSize | u32 defaultValuesOffset 0xffffffff legal when size 0
|
|
212
|
+
u8 textureCount cap 64
|
|
213
|
+
u8 registerIndex | u32 name | u8 type | u32 count | u8 isSRGB | u8 isAutoregister
|
|
214
|
+
u8 samplerCount cap 64
|
|
215
|
+
u8 registerIndex | u32 name | u8 x7 | f32 mipLODBias | u8 maxAnisotropy
|
|
216
|
+
u8 comparisonFunc | f32 borderColor[4] | f32 minLOD | f32 maxLOD | u8 isDynamic
|
|
217
|
+
u8 uavCount cap 64
|
|
218
|
+
u8 registerIndex | u32 name | u8 type | u32 count | u8 isAutoregister
|
|
219
|
+
u8 annotationCount
|
|
220
|
+
u32 name | u8 type | (u32 stringOffset if type == STRING else 4 raw bytes)
|
|
221
|
+
u8 renderStateCount cap 64
|
|
222
|
+
u32 state | u32 value
|
|
223
|
+
u8 libraryCount
|
|
224
|
+
u32 payloadSize | u32 shaderSize | u32 shaderDataOffset
|
|
225
|
+
u32 exportCount u32, not u8
|
|
226
|
+
u8 type | u32 name
|
|
227
|
+
u32 hitGroupName
|
|
228
|
+
StageData globalInputs
|
|
229
|
+
StageData localInputs
|
|
230
|
+
u16 parameterCount cap 256
|
|
231
|
+
u32 name | annotation map as above
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Four places this is easy to get wrong:
|
|
235
|
+
|
|
236
|
+
- **At v15 the program payload comes first and the signature tables follow.**
|
|
237
|
+
`pipelineInputs` and `registers` sit *after* `shaderCode` and `threadGroupSize`.
|
|
238
|
+
Before v14 it was the other way round; v14 moved them, and Carbon marks its own
|
|
239
|
+
v14 branch as uncertain.
|
|
240
|
+
- **A UAV record is one byte shorter than a texture record** — it has no `isSRGB`.
|
|
241
|
+
Carbon's reader hardcodes `isSRGB = false` and the writer omits it. Sharing one
|
|
242
|
+
"resource" codec between the two silently corrupts every subsequent field.
|
|
243
|
+
- **`borderColor` is four floats on a sampler and one byte on a static sampler**
|
|
244
|
+
because the two records mirror different D3D binding models.
|
|
245
|
+
- **A non-string annotation value is four raw bytes.** Carbon writes it through the
|
|
246
|
+
`float` member of a `{float,int32_t}` union and reads it back through a different
|
|
247
|
+
union. The bytes round-trip; applying an int/float conversion does not. The codec
|
|
248
|
+
keeps `rawValue` as bytes for exactly this reason.
|
|
249
|
+
|
|
250
|
+
Carbon writes `textures`, `samplers`, `uavs` and render states in ascending key
|
|
251
|
+
order and sorts annotation keys by bytewise string comparison.
|
|
252
|
+
`compareAnnotationNames` implements that comparison over UTF-8 bytes, which is
|
|
253
|
+
*not* the same as JavaScript's UTF-16 code-unit order for names outside ASCII
|
|
254
|
+
— `"Z"` sorts before `"a"`.
|
|
255
|
+
|
|
256
|
+
### The optional trailing block
|
|
257
|
+
|
|
258
|
+
Our own containers add exactly one optional block per pass, after the render-state
|
|
259
|
+
table, referenced by a `{u32 size, u32 offset}` pair into the arena. A Carbon file
|
|
260
|
+
ends the pass at the render states, so the reader and writer gate it on
|
|
261
|
+
`{ backend: true }` and produce Carbon's bytes unchanged when it is closed.
|
|
262
|
+
|
|
263
|
+
The block carries the two sections that are not derivable from Carbon reflection —
|
|
264
|
+
WebGPU bind-group layouts and resource transforms — in **one** unit, because they
|
|
265
|
+
are mutually required and because "the Carbon region is backend-invariant, with
|
|
266
|
+
exactly one optional trailing block" is the invariant worth keeping.
|
|
267
|
+
|
|
268
|
+
It lives in the arena so identical layouts dedupe across bodies the way program
|
|
269
|
+
source does; measured sharing is 30.5:1 at `(body, pass)` granularity, 22 distinct
|
|
270
|
+
blocks across 672 pairs. That forces one property: **the block contains no arena
|
|
271
|
+
offsets.** An offset is only known after the arena's content sort, which depends on
|
|
272
|
+
every blob's bytes including this one, so a block referencing the arena could not be
|
|
273
|
+
built before it was interned. Strings inside it are inline and length-prefixed.
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
u8 blobVersion = 1
|
|
277
|
+
u8 bindGroupCount
|
|
278
|
+
u8 group | u8 bindingCount
|
|
279
|
+
u8 resourceKind | u8 registerSpace | u8 binding | u8 visibilityMask
|
|
280
|
+
u32 registerIndex | u32 structureStride (0xffffffff absent) | u8 arrayLayerCount (0 absent)
|
|
281
|
+
str type | str generatedSymbol | str transformId (empty = none)
|
|
282
|
+
u8 transformCount
|
|
283
|
+
u8 familyCode | str id | u8 inputCount
|
|
284
|
+
u8 registerSpace | u8 registerIndex | str parameter
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`identity` and `group` on each binding, and a transform's `kind`,
|
|
288
|
+
`stage`, `representation`, `missingLayer`, `viewDimension`, `layerCount`,
|
|
289
|
+
`output.identity`, `output.scopeIdentity`, `output.name`, `layoutKey` and every
|
|
290
|
+
input's `layer` are restored on read, not stored. The family byte is what keeps them
|
|
291
|
+
derivable without pinning the format to one recognizer. `id` and each input's
|
|
292
|
+
`parameter` stay on the wire deliberately — `id` because a caller may supply it,
|
|
293
|
+
`parameter` because it keeps layer identity cross-checkable rather than asserted by
|
|
294
|
+
position.
|
|
295
|
+
|
|
296
|
+
Backend-block version 1 stores visibility but not the original
|
|
297
|
+
`scopeIdentity`. The reader reconstructs `${identity}@${visibility[0]}`. A
|
|
298
|
+
multi-stage shared binding therefore rereads as stage-qualified rather than
|
|
299
|
+
recovering its original bare scope. Callers must not infer that original
|
|
300
|
+
sharing decision from the wire view.
|
|
301
|
+
|
|
302
|
+
An unknown `blobVersion` reports the pass as having no backend data rather than
|
|
303
|
+
misparsing it; the enclosing size makes it skippable.
|
|
304
|
+
|
|
305
|
+
### Count caps
|
|
306
|
+
|
|
307
|
+
`CARBON_EFFECT_COUNT_CAPS` mirrors the runtime's inclusive limits. Carbon's
|
|
308
|
+
compiler enforces none of them while its runtime rejects anything above them,
|
|
309
|
+
so an over-large effect compiles and then fails to load; our writer checks on
|
|
310
|
+
the way out. The caps Carbon does *not* have — techniques, registers, static
|
|
311
|
+
samplers, constants, libraries, exports, annotation counts — are deliberately
|
|
312
|
+
not invented here.
|
|
313
|
+
|
|
314
|
+
### The alias path
|
|
315
|
+
|
|
316
|
+
Carbon compares packed bodies pairwise and points a duplicate's row at the
|
|
317
|
+
surviving twin. The row is **kept**, so the offset table stays dense while the
|
|
318
|
+
file stores each distinct body once. Across the shipped corpus 22% of files
|
|
319
|
+
alias, at roughly 2.1 rows per distinct body.
|
|
320
|
+
|
|
321
|
+
## Offset-table density
|
|
322
|
+
|
|
323
|
+
Carbon indexes the offset table **positionally** and never reads each row's
|
|
324
|
+
stored `index` field. A sparse or misordered table therefore does not fail — it
|
|
325
|
+
silently returns the wrong shader body.
|
|
326
|
+
|
|
327
|
+
Density is incidental in Carbon: it falls out of `g_compiledEffects` being a
|
|
328
|
+
`std::map` densely keyed by the work-queue builder, and is promised nowhere.
|
|
329
|
+
|
|
330
|
+
Measured twice at build 3444265. A header-only sweep of every `.sm_hi`, `.sm_lo`
|
|
331
|
+
and `.sm_depth` under `effect.dx11` and `effect.dx12` — 3222 files, 52,332 rows —
|
|
332
|
+
and the full round-trip run below across all three backends — 4833 files, 78,498
|
|
333
|
+
rows, 40,645 distinct bodies. **Every file is dense and positionally indexed**, and
|
|
334
|
+
every row's byte range lies inside the file and clear of the header. In every
|
|
335
|
+
single file the body region tiles the post-header space exactly, with no leading or
|
|
336
|
+
trailing slack.
|
|
337
|
+
|
|
338
|
+
Given that, the checks are implemented as follows:
|
|
339
|
+
|
|
340
|
+
- `CjsCarbonEffectReader` **always** collects `dense` and `indicesMatchPosition` as
|
|
341
|
+
diagnostics, and **always** fails closed on an out-of-range row.
|
|
342
|
+
- Density and positional indexing **fail closed on read by default**.
|
|
343
|
+
`{ permissive: true }` skips the check and leaves the diagnostics in place, for
|
|
344
|
+
forensic inspection of a file already known to be malformed. It is not a load
|
|
345
|
+
option.
|
|
346
|
+
- `writeCarbonEffectFile` **always** fails closed: it refuses to emit bodies that
|
|
347
|
+
are not dense from index 0. Where we own the bytes there is no reason to be
|
|
348
|
+
lenient.
|
|
349
|
+
|
|
350
|
+
`--ignore-permutations` does make CCP's compiler emit only key 0 while declaring
|
|
351
|
+
every axis, so a sparse file is producible. That argues for the escape hatch, not
|
|
352
|
+
for permissive defaults: Carbon does not reject such a file, it returns the wrong
|
|
353
|
+
permutation's shader silently, which is the failure class this port exists to
|
|
354
|
+
close.
|
|
355
|
+
|
|
356
|
+
## Backend selection and envelope removal
|
|
357
|
+
|
|
358
|
+
**Historical compatibility.** Flat-package builds once prepended
|
|
359
|
+
`magic | containerVersion | payloadKind` before Carbon's byte-compatible
|
|
360
|
+
layout. Current packaging emits no such prefix and the former helper is no
|
|
361
|
+
longer part of the package.
|
|
362
|
+
|
|
363
|
+
**Current WebGPU wire.** CEWGPU bytes have no envelope, magic, `payloadKind`,
|
|
364
|
+
or independent container version. They are bare Carbon v15 records resolved
|
|
365
|
+
from `effect.webgpu/`, with one optional per-pass backend block. `.cewg`
|
|
366
|
+
remains a separate CEWG chunk format rather than this Carbon-record wire.
|
|
367
|
+
|
|
368
|
+
`CewgpuContainer` reads the optional blocks, and the shared record reader can
|
|
369
|
+
auto-detect them from a description's declared size. Direct conversion from
|
|
370
|
+
those descriptions into the portable reflection envelope consumed by
|
|
371
|
+
`Tr2EffectRes` remains an adapter boundary.
|
|
372
|
+
|
|
373
|
+
Versioning remains local to what it versions: Carbon's version dword governs
|
|
374
|
+
the Carbon region, while `blobVersion` governs the optional backend block. An
|
|
375
|
+
unknown block version is skipped rather than misparsed. The package does not
|
|
376
|
+
claim a version in CCP's namespace.
|
|
377
|
+
|
|
378
|
+
Loose program bytes without a resource path can be identified from their
|
|
379
|
+
payload: DXBC opens with `"DXBC"`, AIR is bitcode (`BC 0xC0DE`), and WGSL and
|
|
380
|
+
GLSL have distinct text syntax. The stage record itself carries no language
|
|
381
|
+
tag; program interpretation remains a backend/path responsibility.
|
|
382
|
+
|
|
383
|
+
## Verification
|
|
384
|
+
|
|
385
|
+
`node --test` in this package. Two gates.
|
|
386
|
+
|
|
387
|
+
**Always green.** `test/format/byte-primitives.test.js` and
|
|
388
|
+
`test/format/carbon-effect.test.js` build a synthetic four-permutation v15
|
|
389
|
+
container exercising every record type — static samplers, UAVs, annotations of
|
|
390
|
+
every value type, render states, a raytracing library with both stage-data blocks
|
|
391
|
+
— and assert a byte-exact write → read → write round trip, the arena sort order,
|
|
392
|
+
the caps, the structural checks and, until the switchover, the legacy envelope's
|
|
393
|
+
disjointness.
|
|
394
|
+
|
|
395
|
+
**Env-gated real-file proof.** `test/format/carbon-effect-corpus.test.js`, enabled
|
|
396
|
+
with `CARBON_EFFECT_CORPUS_DIR`. Game bytes are never committed. Supply a
|
|
397
|
+
separately acquired corpus at pinned build 3444265. The test re-emits each file
|
|
398
|
+
three ways:
|
|
399
|
+
|
|
400
|
+
1. every description blob through the file's own arena — proves the field order;
|
|
401
|
+
2. the whole container from raw bodies and the source arena — proves the header
|
|
402
|
+
order, the base arithmetic and the alias path;
|
|
403
|
+
3. the whole container with the arena rebuilt from the references found — proves
|
|
404
|
+
the sorted-offset policy.
|
|
405
|
+
|
|
406
|
+
Only the third can legitimately differ, because an arena may retain blobs the file
|
|
407
|
+
no longer references. When it does differ the divergence is reported exactly and
|
|
408
|
+
asserted to be unreferenced-blob retention; it is never downgraded to a weaker
|
|
409
|
+
comparison such as "same strings, any order", which would look green and prove
|
|
410
|
+
nothing.
|
|
411
|
+
|
|
412
|
+
Measured result over the complete corpus — 4833 files (537 shaders × 3 variants ×
|
|
413
|
+
3 backends), 78,498 offset-table rows, 40,645 distinct description bodies:
|
|
414
|
+
**all three modes byte-exact, with zero arena-rebuild divergences, zero sparse
|
|
415
|
+
tables and zero misordered tables.** Not one shipped file retains an unreferenced
|
|
416
|
+
arena blob, so the sorted-offset policy reproduces CCP's arena exactly.
|
|
417
|
+
Supply a separately acquired corpus and set `CARBON_EFFECT_CORPUS_DIR`; no
|
|
418
|
+
corpus data ships with the package.
|
|
419
|
+
|
|
420
|
+
That result is the container port's central evidence. The same reader and the same
|
|
421
|
+
writer reproduce, byte for byte, files whose program payloads are DXBC in two
|
|
422
|
+
dialects and AIR — with no language field anywhere in the format. The metadata
|
|
423
|
+
region is backend-invariant as a measured fact rather than an argument from the
|
|
424
|
+
writer. Backend selection therefore belongs at the resource-path boundary, not
|
|
425
|
+
in an envelope or per-stage record.
|
|
426
|
+
|
|
427
|
+
`effect.gles2` is deliberately not a validation target for **this package**: those
|
|
428
|
+
shaders are v8, and nothing in the container port reads or writes them.
|
|
429
|
+
|
|
430
|
+
**Do not read that as "obsolete".** `effect.gles2` is the shader tree ccpwgl
|
|
431
|
+
actually renders with today — it is the only one that currently works end to end.
|
|
432
|
+
|
|
433
|
+
The two statements coexist because **v15-only constrains what we write and
|
|
434
|
+
validate against, not what a reader may accept.** Version-branching is the
|
|
435
|
+
format's own mechanism. A reader that wants all supported generations branches
|
|
436
|
+
on the version dword — v2..8 legacy gles2, v15 everything current — which is one
|
|
437
|
+
reader, not a bespoke path per format.
|
|
438
|
+
|
|
439
|
+
**Our containers are v15, not a version of our own.** A "v16" was considered for
|
|
440
|
+
the variant carrying the per-pass backend block and **rejected**: CCP owns that
|
|
441
|
+
number space, so claiming 16 would collide with any real v16 they ship, in the one
|
|
442
|
+
field whose entire job is telling a reader how to parse. It also failed the rule
|
|
443
|
+
the rest of this format is held to — invent something only because it *has to*
|
|
444
|
+
exist, never because we think it should.
|
|
445
|
+
|
|
446
|
+
The container needs no new version. Each description blob carries a declared
|
|
447
|
+
size in the offset table, and [Rule 1](#two-rules-for-anything-added-later)
|
|
448
|
+
already requires it to parse to exactly that end. A reader parses a blob without
|
|
449
|
+
blocks and re-parses with them if the cursor misses the declared end, so the
|
|
450
|
+
presence of the block is **self-describing** with no new field, no container
|
|
451
|
+
version and no out-of-band flag. `blobVersion` inside the block versions the
|
|
452
|
+
extension itself.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# DXBC format documentation
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource/formats/dxbc`
|
|
5
|
+
Audience: Shader-tool authors, lowering-backend authors, and maintainers
|
|
6
|
+
Summary: Explains the pure-JavaScript DXBC reader, its decoded output, and its boundary with effect and shader-lowering packages.
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
`@carbonenginejs/runtime-resource/formats/dxbc` owns pure-JavaScript reading of Microsoft DXBC
|
|
11
|
+
containers. It validates the chunk directory, decodes input/output/patch
|
|
12
|
+
signatures, reads SM4/SM5 program headers, and converts instruction tokens into
|
|
13
|
+
structured records.
|
|
14
|
+
|
|
15
|
+
The package preserves unfamiliar declaration payload words as `tailTokens`
|
|
16
|
+
where the framing is valid. Executable instructions decode strictly and reject
|
|
17
|
+
malformed operand lengths.
|
|
18
|
+
|
|
19
|
+
## Use this package when
|
|
20
|
+
|
|
21
|
+
Use `format-dxbc` when you need:
|
|
22
|
+
|
|
23
|
+
- cheap DXBC identification and container inspection;
|
|
24
|
+
- plain JSON-compatible signatures and instruction records;
|
|
25
|
+
- internal decoder objects for an advanced shader backend; or
|
|
26
|
+
- a browser-safe byte decoder with no native executable or filesystem
|
|
27
|
+
dependency.
|
|
28
|
+
|
|
29
|
+
Do not place target-language lowering rules here. GLSL emission belongs to
|
|
30
|
+
`@carbonenginejs/runtime-resource/formats/webgl`, and WGSL emission belongs to
|
|
31
|
+
`@carbonenginejs/runtime-resource/formats/webgpu`.
|
|
32
|
+
|
|
33
|
+
## Where it fits
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
compiled effects
|
|
37
|
+
|
|
|
38
|
+
v
|
|
39
|
+
format-hlsl
|
|
40
|
+
|
|
|
41
|
+
| opaque DXBC stage bytes
|
|
42
|
+
v
|
|
43
|
+
format-dxbc
|
|
44
|
+
|
|
|
45
|
+
+---- decoded records ----> format-webgl
|
|
46
|
+
|
|
|
47
|
+
+---- decoded records ----> format-webgpu
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The public decoder contract is Microsoft DXBC data. Package-local binary
|
|
51
|
+
utilities are internal implementation details and are not public npm subpaths.
|
|
52
|
+
|
|
53
|
+
## Start here
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import { CjsDxbcFormat } from "@carbonenginejs/runtime-resource/formats/dxbc";
|
|
57
|
+
|
|
58
|
+
const decoded = CjsDxbcFormat.read(shaderBytes, {
|
|
59
|
+
source: "example.dxbc"
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Documentation map
|
|
64
|
+
|
|
65
|
+
- [Architecture and boundaries](architecture.md)
|
|
66
|
+
- [Public API reference](reference/api.md)
|
|
67
|
+
- [Decoded output contract](reference/decoded-output.md)
|
|
68
|
+
- [Class-purpose catalog](reference/classes/README.md)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Architecture and boundaries
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource/formats/dxbc`
|
|
5
|
+
Audience: Shader-tool authors, lowering-backend authors, and maintainers
|
|
6
|
+
Summary: Defines DXBC decoding ownership, strictness, dependency direction, and target-language non-goals.
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
`format-dxbc` turns caller-supplied DXBC bytes into validated container,
|
|
11
|
+
signature, program, operand, declaration, and instruction records. It stops
|
|
12
|
+
before target-language code generation.
|
|
13
|
+
|
|
14
|
+
## Dependency direction
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
caller or format-hlsl
|
|
18
|
+
|
|
|
19
|
+
| DXBC bytes
|
|
20
|
+
v
|
|
21
|
+
format-dxbc
|
|
22
|
+
/ \
|
|
23
|
+
v v
|
|
24
|
+
format-webgl format-webgpu
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The package has no runtime dependency and its public source works in browsers
|
|
28
|
+
and Node. Lowering packages depend on its decoded records; it does not import
|
|
29
|
+
them.
|
|
30
|
+
|
|
31
|
+
## Owned responsibilities
|
|
32
|
+
|
|
33
|
+
- DXBC magic, header, checksum field, total-size, and chunk-directory parsing.
|
|
34
|
+
- Bounds-checked access to chunk payloads.
|
|
35
|
+
- `ISGN`, `ISG1`, `OSGN`, `OSG1`, `OSG5`, `PCSG`, and `PSG1` signature
|
|
36
|
+
records.
|
|
37
|
+
- `SHEX` and `SHDR` program version and token-stream reading.
|
|
38
|
+
- SM4/SM5 opcode, operand, declaration, control, and extension-token decoding.
|
|
39
|
+
- SM5.1 binding-range and resource-reference records.
|
|
40
|
+
- JSON-compatible output and an internal raw-object mode.
|
|
41
|
+
- Structured decode errors with source and offset details.
|
|
42
|
+
|
|
43
|
+
## Ownership elsewhere
|
|
44
|
+
|
|
45
|
+
- Compiled effect containers, permutations, techniques, and binding manifests
|
|
46
|
+
belong to `format-hlsl`.
|
|
47
|
+
- GLSL ES generation and WebGL-specific register/storage policy belong to
|
|
48
|
+
`format-webgl`.
|
|
49
|
+
- WGSL generation, pass-global WebGPU binding allocation, and CEWGPU assembly
|
|
50
|
+
belong to `format-webgpu`.
|
|
51
|
+
- GPU shader-module and pipeline realization belongs to engine packages.
|
|
52
|
+
|
|
53
|
+
## Strictness
|
|
54
|
+
|
|
55
|
+
The reader validates container and chunk bounds before exposing data.
|
|
56
|
+
Executable instructions must consume exactly their declared token length.
|
|
57
|
+
Declarations decode the payload forms implemented by the package and retain
|
|
58
|
+
remaining valid words in `tailTokens`, allowing unusual stages to remain
|
|
59
|
+
inspectable without pretending every declaration payload has a specialized
|
|
60
|
+
projection.
|
|
61
|
+
|
|
62
|
+
The opcode-name table covers the SM4/SM5 vocabulary used for framing. That does
|
|
63
|
+
not imply every opcode has a target-language implementation in every lowering
|
|
64
|
+
package.
|
|
65
|
+
|
|
66
|
+
## Output stability
|
|
67
|
+
|
|
68
|
+
`emit: "json"` is the stable integration surface. It returns plain data and
|
|
69
|
+
converts typed arrays into number arrays.
|
|
70
|
+
|
|
71
|
+
`emit: "raw"` exposes package-internal class instances for advanced backends.
|
|
72
|
+
Those classes are not public npm exports; consumers should treat their concrete
|
|
73
|
+
constructors as internal and depend only on the fields used by the owning
|
|
74
|
+
lowering package.
|
|
75
|
+
|
|
76
|
+
## Related documentation
|
|
77
|
+
|
|
78
|
+
- [Package documentation](README.md)
|
|
79
|
+
- [Public API reference](reference/api.md)
|
|
80
|
+
- [Decoded output contract](reference/decoded-output.md)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Public API reference
|
|
2
|
+
|
|
3
|
+
Status: Evolving
|
|
4
|
+
Scope: `@carbonenginejs/runtime-resource/formats/dxbc`
|
|
5
|
+
Audience: Shader-tool authors and lowering-backend authors
|
|
6
|
+
Summary: Defines the public `CjsDxbcFormat` profile, one-shot helpers, options, and failure behavior.
|
|
7
|
+
|
|
8
|
+
## Export
|
|
9
|
+
|
|
10
|
+
The package root exports `CjsDxbcFormat` as both a named and default export:
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import CjsDxbcFormat, {
|
|
14
|
+
CjsDxbcFormat as DxbcFormat
|
|
15
|
+
} from "@carbonenginejs/runtime-resource/formats/dxbc";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
No internal container, signature, program, decoder, or error class is exposed
|
|
19
|
+
through the package export map.
|
|
20
|
+
|
|
21
|
+
## Reusable profile
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
const reader = new DxbcFormat({
|
|
25
|
+
emit: "json",
|
|
26
|
+
source: "example.dxbc",
|
|
27
|
+
decodeInstructions: true
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const decoded = reader.Read(shaderBytes);
|
|
31
|
+
const summary = reader.Inspect(shaderBytes);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Instance method | Purpose |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `SetValues(options)` | Merges reusable profile defaults and returns the profile. |
|
|
37
|
+
| `GetValues(options?)` | Returns effective values with optional per-call overrides. |
|
|
38
|
+
| `Read(bytes, options?)` | Reads one DXBC payload using the effective profile. |
|
|
39
|
+
| `Inspect(bytes, options?)` | Returns container, stage, shader-model, and signature counts without instruction decoding. |
|
|
40
|
+
|
|
41
|
+
## One-shot helpers
|
|
42
|
+
|
|
43
|
+
| Static helper | Purpose |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `isDxbc(bytes)` | Returns `true` when input starts with the DXBC magic; invalid input returns `false`. |
|
|
46
|
+
| `read(bytes, options?)` | Reads one DXBC payload. |
|
|
47
|
+
| `inspect(bytes, options?)` | Inspects one payload without instruction decoding. |
|
|
48
|
+
| `toJSON(value)` | Deep-converts supported values to JSON-compatible data. |
|
|
49
|
+
|
|
50
|
+
The class also exposes `OUTPUT_JSON`, `OUTPUT_RAW`, input/output media metadata,
|
|
51
|
+
and the format's supported input type.
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
| Option | Default | Meaning |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `emit` | `"json"` | `"json"` returns plain data; `"raw"` returns internal decoder objects. |
|
|
58
|
+
| `source` | `"memory"` | Caller-owned label included in error details; no path is opened. |
|
|
59
|
+
| `decodeInstructions` | `true` | When false, reads the container, signatures, and program header without decoding instructions. |
|
|
60
|
+
|
|
61
|
+
Inputs may be `Uint8Array`, `ArrayBuffer`, Node `Buffer`, `DataView`, or another
|
|
62
|
+
array-buffer view.
|
|
63
|
+
|
|
64
|
+
## Errors
|
|
65
|
+
|
|
66
|
+
Invalid options and unsupported input types throw `TypeError`. Malformed DXBC
|
|
67
|
+
throws an internal `DxbcReadError` carrying a stable human-readable message and
|
|
68
|
+
a `details` record with relevant source, offset, size, chunk, or opcode
|
|
69
|
+
information.
|
|
70
|
+
|
|
71
|
+
`isDxbc` is the non-throwing sniff operation.
|
|
72
|
+
|
|
73
|
+
## Related documentation
|
|
74
|
+
|
|
75
|
+
- [Decoded output contract](decoded-output.md)
|
|
76
|
+
- [Architecture and boundaries](../architecture.md)
|
|
77
|
+
- [Class-purpose catalog](classes/README.md)
|