@carbonenginejs/runtime-resource 0.12.0 → 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.
Files changed (170) hide show
  1. package/dist/CjsResMan.js +4 -4
  2. package/dist/CjsResMan.js.map +1 -1
  3. package/dist/_virtual/_rollupPluginBabelHelpers.js +1 -4
  4. package/dist/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  5. package/dist/format/CjsByteReader.js +310 -0
  6. package/dist/format/CjsByteReader.js.map +1 -0
  7. package/dist/format/CjsByteWriter.js +242 -0
  8. package/dist/format/CjsByteWriter.js.map +1 -0
  9. package/dist/format/CjsFormatError.js +41 -0
  10. package/dist/format/CjsFormatError.js.map +1 -0
  11. package/dist/format/CjsStringTable.js +268 -0
  12. package/dist/format/CjsStringTable.js.map +1 -0
  13. package/dist/format/carbonEffect/CjsCarbonEffectReader.js +361 -0
  14. package/dist/format/carbonEffect/CjsCarbonEffectReader.js.map +1 -0
  15. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js +373 -0
  16. package/dist/format/carbonEffect/CjsCarbonEffectWriter.js.map +1 -0
  17. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js +372 -0
  18. package/dist/format/carbonEffect/carbonDescriptionFromPortable.js.map +1 -0
  19. package/dist/format/carbonEffect/carbonEffectBackendBlock.js +427 -0
  20. package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -0
  21. package/dist/format/carbonEffect/carbonEffectRecords.js +955 -0
  22. package/dist/format/carbonEffect/carbonEffectRecords.js.map +1 -0
  23. package/dist/format/compareUtf8.js +36 -0
  24. package/dist/format/compareUtf8.js.map +1 -0
  25. package/dist/format/index.js +11 -0
  26. package/dist/format/index.js.map +1 -0
  27. package/dist/formats/bnk/CjsBnkFormat.js +10 -4
  28. package/dist/formats/bnk/CjsBnkFormat.js.map +1 -1
  29. package/dist/formats/bnk/core/eventAction.js +305 -0
  30. package/dist/formats/bnk/core/eventAction.js.map +1 -0
  31. package/dist/formats/bnk/core/helpers.js +11 -2
  32. package/dist/formats/bnk/core/helpers.js.map +1 -1
  33. package/dist/formats/bnk/core/nodeBase.js +532 -0
  34. package/dist/formats/bnk/core/nodeBase.js.map +1 -0
  35. package/dist/formats/bnk/core/sfxNodes.js +252 -152
  36. package/dist/formats/bnk/core/sfxNodes.js.map +1 -1
  37. package/dist/formats/hlsl/core/HlslReader.js +7 -257
  38. package/dist/formats/hlsl/core/HlslReader.js.map +1 -1
  39. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js +781 -0
  40. package/dist/formats/hlsl/core/carbonDescriptionToRuntime.js.map +1 -0
  41. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +21 -1
  42. package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -1
  43. package/dist/formats/index.js +1 -1
  44. package/dist/formats/webgl/core/cewg/CewgPackage.js +2 -1
  45. package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -1
  46. package/dist/formats/webgl/core/cewg/binary.js +11 -93
  47. package/dist/formats/webgl/core/cewg/binary.js.map +1 -1
  48. package/dist/formats/webgl/core/effectPackage.js +1 -1
  49. package/dist/formats/webgpu/CjsWebgpuFormat.js +4 -23
  50. package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -1
  51. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js +197 -0
  52. package/dist/formats/webgpu/core/buildCarbonEffectContainer.js.map +1 -0
  53. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js +368 -0
  54. package/dist/formats/webgpu/core/cewgpu/CewgpuContainer.js.map +1 -0
  55. package/dist/formats/webgpu/core/cewgpu/containerViews.js +355 -0
  56. package/dist/formats/webgpu/core/cewgpu/containerViews.js.map +1 -0
  57. package/dist/formats/webgpu/core/cewgpu/validateContainer.js +90 -0
  58. package/dist/formats/webgpu/core/cewgpu/validateContainer.js.map +1 -0
  59. package/dist/formats/webgpu/core/effectBackendBodySet.js +21 -20
  60. package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -1
  61. package/dist/formats/webgpu/core/helpers.js +87 -92
  62. package/dist/formats/webgpu/core/helpers.js.map +1 -1
  63. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +14 -13
  64. package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -1
  65. package/dist/formats/webgpu/core/ir/inferValueTypes.js +10 -8
  66. package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -1
  67. package/dist/formats/webgpu/core/packageEffect.js +94 -9
  68. package/dist/formats/webgpu/core/packageEffect.js.map +1 -1
  69. package/dist/formats/webgpu/core/packageMetadata.js +10 -1
  70. package/dist/formats/webgpu/core/packageMetadata.js.map +1 -1
  71. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +17 -15
  72. package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -1
  73. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +13 -12
  74. package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -1
  75. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +9 -8
  76. package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -1
  77. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +23 -22
  78. package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -1
  79. package/dist/formats/webgpu/core/wgsl/selectionPlans.js +46 -44
  80. package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -1
  81. package/dist/resource/CjsResource.js +700 -684
  82. package/dist/resource/CjsResource.js.map +1 -1
  83. package/dist/resource/Tr2LightProfileRes.js +18 -27
  84. package/dist/resource/Tr2LightProfileRes.js.map +1 -1
  85. package/dist/resource/audio/CjsAudioBufferRes.js +2 -2
  86. package/dist/resource/audio/CjsAudioBufferRes.js.map +1 -1
  87. package/dist/resource/audio/CjsAudioRes.js +2 -2
  88. package/dist/resource/audio/CjsAudioRes.js.map +1 -1
  89. package/dist/resource/geometry/TriGeometryRes.js +605 -586
  90. package/dist/resource/geometry/TriGeometryRes.js.map +1 -1
  91. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js +21 -30
  92. package/dist/resource/geometry/granny/Tr2GrannyStateRes.js.map +1 -1
  93. package/dist/resource/geometry/granny/TriGrannyRes.js +21 -30
  94. package/dist/resource/geometry/granny/TriGrannyRes.js.map +1 -1
  95. package/dist/resource/shader/Tr2EffectRes.js +9 -7
  96. package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
  97. package/dist/resource/shader/Tr2MaterialArea.js +5 -3
  98. package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
  99. package/dist/resource/shader/Tr2MaterialMesh.js +4 -2
  100. package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
  101. package/dist/resource/shader/Tr2MaterialRes.js +5 -3
  102. package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
  103. package/dist/resource/shader/Tr2Shader.js +26 -21
  104. package/dist/resource/shader/Tr2Shader.js.map +1 -1
  105. package/dist/resource/shader/Tr2ShaderPermutation.js +8 -6
  106. package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
  107. package/dist/resource/shader/reflection/Tr2EffectDefine.js +5 -3
  108. package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
  109. package/dist/resource/shader/reflection/Tr2EffectDescription.js +5 -3
  110. package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
  111. package/dist/resource/shader/reflection/Tr2EffectLibrary.js +17 -15
  112. package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
  113. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +11 -9
  114. package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
  115. package/dist/resource/shader/reflection/Tr2EffectResource.js +8 -6
  116. package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
  117. package/dist/resource/shader/reflection/Tr2EffectStageInput.js +15 -13
  118. package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
  119. package/dist/resource/shader/reflection/Tr2EffectTechnique.js +7 -5
  120. package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
  121. package/dist/resource/shader/reflection/Tr2Pass.js +11 -9
  122. package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
  123. package/dist/resource/shader/sampler/Tr2SamplerSetup.js +8 -6
  124. package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
  125. package/dist/resource/texture/CjsTextureArrayRes.js +400 -411
  126. package/dist/resource/texture/CjsTextureArrayRes.js.map +1 -1
  127. package/dist/resource/texture/Tr2ImageRes.js +99 -95
  128. package/dist/resource/texture/Tr2ImageRes.js.map +1 -1
  129. package/dist/resource/texture/TriTextureRes.js +312 -273
  130. package/dist/resource/texture/TriTextureRes.js.map +1 -1
  131. package/docs/README.md +13 -1
  132. package/docs/architecture.md +3 -3
  133. package/docs/concepts/resource-lifecycle.md +9 -2
  134. package/docs/concepts/shader-resource-model.md +114 -0
  135. package/docs/concepts/writing-an-engine-adapter.md +115 -0
  136. package/docs/formats/README.md +24 -1
  137. package/docs/formats/carbon-effect-container.md +452 -0
  138. package/docs/formats/dxbc/reference/classes/README.md +5 -72
  139. package/docs/formats/hlsl/reference/api.md +5 -4
  140. package/docs/formats/hlsl/reference/classes/README.md +6 -11
  141. package/docs/formats/provenance.md +23 -13
  142. package/docs/formats/webgl/reference/classes/README.md +5 -92
  143. package/docs/formats/webgpu/README.md +19 -16
  144. package/docs/formats/webgpu/architecture.md +15 -12
  145. package/docs/formats/webgpu/formats/cewgpu.md +175 -438
  146. package/docs/formats/webgpu/guides/effect-packaging.md +132 -140
  147. package/docs/formats/webgpu/reference/api.md +131 -131
  148. package/docs/formats/webgpu/reference/classes/README.md +5 -62
  149. package/docs/formats/webgpu/reference/wgsl-compatibility.md +146 -45
  150. package/docs/formats/wwise.md +34 -8
  151. package/docs/reference/classes/core.md +80 -0
  152. package/docs/reference/classes/formats.md +18 -28
  153. package/docs/reference/events.md +25 -0
  154. package/docs/reference/motherlode-cache.md +22 -8
  155. package/docs/reference/workers.md +5 -5
  156. package/docs/roadmap.md +61 -41
  157. package/package.json +2 -1
  158. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +0 -415
  159. package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +0 -1
  160. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +0 -100
  161. package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +0 -1
  162. package/dist/formats/webgpu/core/cewgpu/binary.js +0 -93
  163. package/dist/formats/webgpu/core/cewgpu/binary.js.map +0 -1
  164. package/dist/formats/webgpu/core/cewgpu/tags.js +0 -17
  165. package/dist/formats/webgpu/core/cewgpu/tags.js.map +0 -1
  166. package/dist/formats/webgpu/core/effectPackageValidation.js +0 -1078
  167. package/dist/formats/webgpu/core/effectPackageValidation.js.map +0 -1
  168. package/docs/formats/hlsl/reference/classes/carbon-compatibility.md +0 -66
  169. package/docs/formats/hlsl/reference/classes/public-api.md +0 -26
  170. package/docs/formats/hlsl/reference/classes/tr2-effect-model.md +0 -150
@@ -0,0 +1,355 @@
1
+ import { HlslEffectBindingManifest } from '../../../hlsl/core/tr2/shader/HlslEffectBindingManifest.js';
2
+ import { HlslShaderBytecode } from '../../../hlsl/core/HlslShaderBytecode.js';
3
+ import { hlslShaderStageName } from '../../../hlsl/core/tr2/HlslRenderContextEnum.js';
4
+ import { runtimeDescriptionFromCarbon } from '../../../hlsl/core/carbonDescriptionToRuntime.js';
5
+ import { buildEffectAnalysis } from '../helpers.js';
6
+ import { WGSL_SET_VERSION } from '../wgsl/buildWgslSet.js';
7
+ import { sha256Utf8 } from '../../../../format/effect/sha256.js';
8
+
9
+ /**
10
+ * Derived compatibility views over a container.
11
+ *
12
+ * The chunk package stored `ANLS` and `WGSL` as documents beside the reflection
13
+ * they were computed from, and carried digests to detect the two disagreeing.
14
+ * The container stores one document, so these are **views**: functions of the
15
+ * records, computed on read. The class of bug the digests guarded cannot occur,
16
+ * because there is no second copy to drift.
17
+ *
18
+ * The analysis view is deliberately not a reimplementation. It runs
19
+ * `buildEffectAnalysis` — the same function the packager runs on source — over a
20
+ * description rebuilt by `runtimeDescriptionFromCarbon`. That is what keeps the
21
+ * view honest: if it were rewritten to emit the fields consumers read today, it
22
+ * would be free to drift from the real analysis, and nothing would notice.
23
+ * `carbon-analysis-adapter-corpus.test.mjs` diffs the two over every shipped
24
+ * permutation and finds zero differences.
25
+ *
26
+ * These views exist for `engine-webgpu`, which reads format-package JSON for
27
+ * Carbon reflection instead of reading `Tr2Shader`. That is a recorded layering
28
+ * defect and is not this port's to fix; the views keep it working unchanged
29
+ * while the wire format moves underneath it. They go away with that cleanup.
30
+ */
31
+
32
+ /**
33
+ * Resolves the permutation Carbon's own `GetShader()` would select with no
34
+ * option overrides.
35
+ *
36
+ * Carbon multiplies each axis's default option by the running product of the
37
+ * preceding axes' option counts. The chunk package stored a single selected
38
+ * body and its index; the container carries every permutation, so the view has
39
+ * to name a default rather than inherit one, and Carbon's own default is the
40
+ * only defensible choice.
41
+ *
42
+ * @param {object} container Loaded container.
43
+ * @returns {number} Permutation index.
44
+ */
45
+ function defaultPermutationIndex(container) {
46
+ let multiplier = 1;
47
+ let index = 0;
48
+ for (const axis of container.carbon.permutations) {
49
+ index += axis.defaultOption * multiplier;
50
+ multiplier *= axis.options.length || 1;
51
+ }
52
+ return index < container.carbon.records.length ? index : 0;
53
+ }
54
+
55
+ /**
56
+ * Rebuilds the runtime effect description for one permutation.
57
+ *
58
+ * @param {object} container Loaded container.
59
+ * @param {number} permutationIndex Permutation index.
60
+ * @param {string} source Source label.
61
+ * @returns {object} Runtime-shaped effect description.
62
+ */
63
+ function describeRuntime(container, permutationIndex, source) {
64
+ return runtimeDescriptionFromCarbon(container.GetDescription(permutationIndex), {
65
+ effectName: source,
66
+ version: container.carbon.version,
67
+ bytecodeFor: (stage, stageType) => new HlslShaderBytecode({
68
+ stageType,
69
+ // Carbon's six stage names, not WebGPU's three. A three-entry table
70
+ // reports a geometry stage as null, which the corpus caught.
71
+ stageName: hlslShaderStageName(stageType),
72
+ bytes: stage.shaderData.bytes,
73
+ shaderSize: stage.shaderData.size,
74
+ stringTableOffset: stage.shaderData.offset,
75
+ effectName: source
76
+ })
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Derives the analysis document for one permutation.
82
+ *
83
+ * One field is honestly different from the stored `ANLS` it replaces:
84
+ * `stages[].shaderBytecode` describes the **WGSL** program the container
85
+ * carries, where the stored document described the source DXBC. The container
86
+ * does not carry DXBC, so reproducing the old numbers would mean inventing
87
+ * them. Nothing in the engine's read surface uses this field; it is a
88
+ * diagnostic.
89
+ *
90
+ * @param {object} container Loaded container.
91
+ * @param {object} [options] View options.
92
+ * @param {string} [options.source] Source label.
93
+ * @param {number} [options.permutationIndex] Permutation to describe.
94
+ * @returns {object} Analysis document.
95
+ */
96
+ function deriveAnalysis(container, options = {}) {
97
+ const source = options.source || container.sourcePath || "memory";
98
+ const permutationIndex = options.permutationIndex ?? defaultPermutationIndex(container);
99
+ const effectDescription = describeRuntime(container, permutationIndex, source);
100
+ return buildEffectAnalysis({
101
+ effectDescription,
102
+ bindingManifest: HlslEffectBindingManifest.fromEffectDescription(effectDescription, {
103
+ effectName: source
104
+ }),
105
+ effectRes: {
106
+ sourcePath: source,
107
+ m_compilerVersion: null
108
+ },
109
+ selection: {
110
+ bodyIndex: permutationIndex,
111
+ selectedOptions: container.carbon.permutations.map(axis => ({
112
+ name: axis.name.value,
113
+ value: axis.options[axis.defaultOption]?.value ?? null
114
+ }))
115
+ }
116
+ }, {
117
+ source,
118
+ decodeBytecode: false,
119
+ decodeInstructions: false
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Derives the WGSL set document for one permutation.
125
+ *
126
+ * `sourceMap` is absent rather than empty, and that is not an oversight: its
127
+ * offsets index DXBC bytes the container does not carry, so it is a translator
128
+ * diagnostic and was dropped on write. It was separately measured to suppress
129
+ * no sharing, so keeping it would have bought nothing either.
130
+ *
131
+ * @param {object} container Loaded container.
132
+ * @param {object} [options] View options.
133
+ * @param {number} [options.permutationIndex] Permutation to describe.
134
+ * @returns {object} WGSL set document.
135
+ */
136
+ function deriveWgsl(container, options = {}) {
137
+ const permutationIndex = options.permutationIndex ?? defaultPermutationIndex(container);
138
+ const body = container.GetBackendBodyPrograms(permutationIndex);
139
+ const shaders = [];
140
+ const layouts = [];
141
+ const resourceTransforms = [];
142
+ for (const pass of body?.passes ?? []) {
143
+ for (const shader of pass.shaders) {
144
+ shaders.push({
145
+ key: shader.key,
146
+ techniqueName: shader.techniqueName,
147
+ passIndex: shader.passIndex,
148
+ stageName: shader.stageName,
149
+ stage: shader.stage,
150
+ stageType: shader.stageType,
151
+ entryPoint: shader.entryPoint,
152
+ code: shader.code,
153
+ ...(shader.threadGroupSize ? {
154
+ threadGroupSize: [...shader.threadGroupSize]
155
+ } : {})
156
+ });
157
+ }
158
+ for (const layout of pass.layouts) layouts.push(layout);
159
+ for (const transform of pass.resourceTransforms ?? []) resourceTransforms.push(transform);
160
+ }
161
+ return {
162
+ format: "CJS_WGSL_SET",
163
+ formatVersion: WGSL_SET_VERSION,
164
+ shaders,
165
+ layouts,
166
+ ...(resourceTransforms.length ? {
167
+ resourceTransforms
168
+ } : {})
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Derives the `WGSB` body-set view the chunk package used to store.
174
+ *
175
+ * `engine-webgpu` resolves a permutation's translated programs through this,
176
+ * which is part of the recorded layering defect -- it should read the shader,
177
+ * not a package document. The view keeps that path working unchanged while the
178
+ * wire format moves underneath it, and goes away with the cleanup.
179
+ *
180
+ * Nothing here is stored. A body's status is whether its stages carry programs,
181
+ * its passes are the record tree's passes, and a pass unit is that pass's
182
+ * shaders and layouts. Bodies are keyed by distinct stored offset, which is
183
+ * exactly what Carbon's alias dedupe produces.
184
+ *
185
+ * @param {object} container Loaded container.
186
+ * @returns {object} Body-set document.
187
+ */
188
+ function deriveBackendBodySet(container) {
189
+ const bodyKeys = container.bodyKeyByOffset;
190
+ const passUnits = [];
191
+ const bodies = [];
192
+ const seen = new Set();
193
+ for (let index = 0; index < container.carbon.records.length; index += 1) {
194
+ const offset = container.carbon.records[index].offset;
195
+ if (seen.has(offset)) continue;
196
+ seen.add(offset);
197
+ const bodyKey = bodyKeys.get(offset);
198
+ const body = container.GetBackendBodyPrograms(index);
199
+ if (!body || body.status !== "translated") {
200
+ bodies.push({
201
+ bodyKey,
202
+ representativePermutationIndex: index,
203
+ status: "unsupported",
204
+ error: body?.error ?? "body carries no translated programs",
205
+ passCount: 0,
206
+ passes: []
207
+ });
208
+ continue;
209
+ }
210
+ const passes = [];
211
+ for (const pass of body.passes) {
212
+ const unit = {
213
+ key: `unit${passUnits.length}`,
214
+ // Hashed from the unit's own content. The chunk body set stored
215
+ // this digest to detect a unit drifting from what referenced it;
216
+ // derived from the content it identifies, it cannot.
217
+ sha256: sha256Utf8(`${JSON.stringify({
218
+ shaders: pass.shaders,
219
+ layouts: pass.layouts,
220
+ resourceTransforms: pass.resourceTransforms ?? []
221
+ })}
222
+ `),
223
+ wgslSetVersion: WGSL_SET_VERSION,
224
+ shaders: pass.shaders,
225
+ layouts: pass.layouts,
226
+ ...(pass.resourceTransforms?.length ? {
227
+ resourceTransforms: pass.resourceTransforms
228
+ } : {})
229
+ };
230
+ passUnits.push(unit);
231
+ passes.push({
232
+ passKey: pass.passKey,
233
+ unitKey: unit.key
234
+ });
235
+ }
236
+ bodies.push({
237
+ bodyKey,
238
+ representativePermutationIndex: index,
239
+ status: "translated",
240
+ error: null,
241
+ passCount: passes.length,
242
+ passes
243
+ });
244
+ }
245
+ return {
246
+ format: "CJS_WGSL_BODY_SET",
247
+ formatVersion: 1,
248
+ bodyCount: bodies.length,
249
+ translatedBodyCount: bodies.filter(body => body.status === "translated").length,
250
+ passUnitCount: passUnits.length,
251
+ passUnits,
252
+ bodies
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Finds the permutation a package resolves to.
258
+ *
259
+ * A "selected" package is not a different format -- the container always carries
260
+ * every permutation. What makes it selected is that exactly one body was
261
+ * translated, so the resolved permutation is the first one whose body carries
262
+ * programs. When every body is translated, there is nothing to single out and
263
+ * Carbon's own default applies.
264
+ *
265
+ * @param {object} container Loaded container.
266
+ * @returns {number} Permutation index.
267
+ */
268
+ function resolvedPermutationIndex(container) {
269
+ const translated = [];
270
+ const seen = new Set();
271
+ for (let index = 0; index < container.carbon.records.length; index += 1) {
272
+ const offset = container.carbon.records[index].offset;
273
+ if (seen.has(offset)) continue;
274
+ seen.add(offset);
275
+ const body = container.GetBackendBodyPrograms(index);
276
+ if (body?.status === "translated") translated.push(index);
277
+ if (translated.length > 1) return defaultPermutationIndex(container);
278
+ }
279
+ return translated.length === 1 ? translated[0] : defaultPermutationIndex(container);
280
+ }
281
+
282
+ /**
283
+ * Derives the `META`-shaped view the chunk package used to store.
284
+ *
285
+ * Every field here is recovered from the records rather than carried:
286
+ * `selectedOptions` from the resolved permutation's option indices, and
287
+ * `wgslSelection` from which passes and stages actually hold programs. Build-time
288
+ * *policy* -- `bodyMode`, `sourceIdentity`, `completeness` -- is deliberately not
289
+ * here: it describes how the artifact was produced, not what it is, and Carbon
290
+ * stores none of it.
291
+ *
292
+ * @param {object} container Loaded container.
293
+ * @param {object} [options] View options.
294
+ * @param {string} [options.source] Source label.
295
+ * @param {number} [options.permutationIndex] Permutation to describe.
296
+ * @returns {object} Metadata document.
297
+ */
298
+ function deriveMetadata(container, options = {}) {
299
+ const source = options.source || container.sourcePath || "memory";
300
+ const permutationIndex = options.permutationIndex ?? resolvedPermutationIndex(container);
301
+ const variant = container.permutationGraph.variants[permutationIndex];
302
+ const axes = container.carbon.permutations;
303
+ const selectedOptions = axes.map((axis, axisIndex) => ({
304
+ name: axis.name.value,
305
+ value: axis.options[variant?.optionIndices?.[axisIndex] ?? axis.defaultOption]?.value ?? null
306
+ }));
307
+ const body = container.GetBackendBodyPrograms(permutationIndex);
308
+ const selectedStageKeys = (body?.passes ?? []).flatMap(pass => pass.shaders.map(shader => shader.key));
309
+ const passKeys = (body?.passes ?? []).map(pass => pass.passKey);
310
+ return {
311
+ effectName: source,
312
+ sourcePath: source,
313
+ bodyIndex: permutationIndex,
314
+ selectedOptions,
315
+ ...(passKeys.length === 1 ? {
316
+ wgslSelection: {
317
+ mode: "explicit",
318
+ completePasses: true,
319
+ techniqueName: passKeys[0].slice(0, passKeys[0].lastIndexOf(".pass")),
320
+ passIndex: Number(/\.pass([0-9]+)$/u.exec(passKeys[0])?.[1] ?? 0),
321
+ requestedStageNames: Array.from(new Set((body?.passes ?? []).flatMap(pass => pass.shaders.map(shader => shader.stageName)))),
322
+ selectedStageKeys
323
+ }
324
+ } : {})
325
+ };
326
+ }
327
+
328
+ /**
329
+ * Derives the `INFO`-shaped summary the chunk package used to store.
330
+ *
331
+ * Everything here is counted from the records rather than trusted from a stored
332
+ * field, which is why no digest accompanies it: a count derived from the thing
333
+ * it counts cannot disagree with it.
334
+ *
335
+ * @param {object} container Loaded container.
336
+ * @param {object} [options] View options.
337
+ * @param {string} [options.source] Source label.
338
+ * @returns {object} Info document.
339
+ */
340
+ function deriveInfo(container, options = {}) {
341
+ const source = options.source || container.sourcePath || "memory";
342
+ const graph = container.permutationGraph;
343
+ return {
344
+ format: "CEWGPU",
345
+ packageKind: "tr2-effect-webgpu",
346
+ sourcePath: source,
347
+ effectVersion: container.carbon.version,
348
+ compilerVersion: [...container.carbon.compilerVersion],
349
+ permutationCount: graph.variants.length,
350
+ uniqueBodyCount: graph.bodies.length
351
+ };
352
+ }
353
+
354
+ export { defaultPermutationIndex, deriveAnalysis, deriveBackendBodySet, deriveInfo, deriveMetadata, deriveWgsl, resolvedPermutationIndex };
355
+ //# sourceMappingURL=containerViews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerViews.js","sources":["../../../../../../src/formats/webgpu/core/cewgpu/containerViews.js"],"sourcesContent":["import { HlslEffectBindingManifest } from \"../../../hlsl/core/tr2/shader/HlslEffectBindingManifest.js\";\nimport { HlslShaderBytecode } from \"../../../hlsl/core/HlslShaderBytecode.js\";\nimport { hlslShaderStageName } from \"../../../hlsl/core/tr2/HlslRenderContextEnum.js\";\nimport { runtimeDescriptionFromCarbon } from \"../../../hlsl/core/carbonDescriptionToRuntime.js\";\nimport { buildEffectAnalysis } from \"../helpers.js\";\nimport { WGSL_SET_VERSION } from \"../wgsl/buildWgslSet.js\";\nimport { sha256Utf8 } from \"../../../../format/effect/sha256.js\";\n\n/**\n * Derived compatibility views over a container.\n *\n * The chunk package stored `ANLS` and `WGSL` as documents beside the reflection\n * they were computed from, and carried digests to detect the two disagreeing.\n * The container stores one document, so these are **views**: functions of the\n * records, computed on read. The class of bug the digests guarded cannot occur,\n * because there is no second copy to drift.\n *\n * The analysis view is deliberately not a reimplementation. It runs\n * `buildEffectAnalysis` — the same function the packager runs on source — over a\n * description rebuilt by `runtimeDescriptionFromCarbon`. That is what keeps the\n * view honest: if it were rewritten to emit the fields consumers read today, it\n * would be free to drift from the real analysis, and nothing would notice.\n * `carbon-analysis-adapter-corpus.test.mjs` diffs the two over every shipped\n * permutation and finds zero differences.\n *\n * These views exist for `engine-webgpu`, which reads format-package JSON for\n * Carbon reflection instead of reading `Tr2Shader`. That is a recorded layering\n * defect and is not this port's to fix; the views keep it working unchanged\n * while the wire format moves underneath it. They go away with that cleanup.\n */\n\n/**\n * Resolves the permutation Carbon's own `GetShader()` would select with no\n * option overrides.\n *\n * Carbon multiplies each axis's default option by the running product of the\n * preceding axes' option counts. The chunk package stored a single selected\n * body and its index; the container carries every permutation, so the view has\n * to name a default rather than inherit one, and Carbon's own default is the\n * only defensible choice.\n *\n * @param {object} container Loaded container.\n * @returns {number} Permutation index.\n */\nexport function defaultPermutationIndex(container)\n{\n let multiplier = 1;\n let index = 0;\n for (const axis of container.carbon.permutations)\n {\n index += axis.defaultOption * multiplier;\n multiplier *= axis.options.length || 1;\n }\n return index < container.carbon.records.length ? index : 0;\n}\n\n/**\n * Rebuilds the runtime effect description for one permutation.\n *\n * @param {object} container Loaded container.\n * @param {number} permutationIndex Permutation index.\n * @param {string} source Source label.\n * @returns {object} Runtime-shaped effect description.\n */\nfunction describeRuntime(container, permutationIndex, source)\n{\n return runtimeDescriptionFromCarbon(container.GetDescription(permutationIndex), {\n effectName: source,\n version: container.carbon.version,\n bytecodeFor: (stage, stageType) => new HlslShaderBytecode({\n stageType,\n // Carbon's six stage names, not WebGPU's three. A three-entry table\n // reports a geometry stage as null, which the corpus caught.\n stageName: hlslShaderStageName(stageType),\n bytes: stage.shaderData.bytes,\n shaderSize: stage.shaderData.size,\n stringTableOffset: stage.shaderData.offset,\n effectName: source\n })\n });\n}\n\n/**\n * Derives the analysis document for one permutation.\n *\n * One field is honestly different from the stored `ANLS` it replaces:\n * `stages[].shaderBytecode` describes the **WGSL** program the container\n * carries, where the stored document described the source DXBC. The container\n * does not carry DXBC, so reproducing the old numbers would mean inventing\n * them. Nothing in the engine's read surface uses this field; it is a\n * diagnostic.\n *\n * @param {object} container Loaded container.\n * @param {object} [options] View options.\n * @param {string} [options.source] Source label.\n * @param {number} [options.permutationIndex] Permutation to describe.\n * @returns {object} Analysis document.\n */\nexport function deriveAnalysis(container, options = {})\n{\n const source = options.source || container.sourcePath || \"memory\";\n const permutationIndex = options.permutationIndex ?? defaultPermutationIndex(container);\n const effectDescription = describeRuntime(container, permutationIndex, source);\n\n return buildEffectAnalysis({\n effectDescription,\n bindingManifest: HlslEffectBindingManifest.fromEffectDescription(effectDescription, {\n effectName: source\n }),\n effectRes: {\n sourcePath: source,\n m_compilerVersion: null\n },\n selection: {\n bodyIndex: permutationIndex,\n selectedOptions: container.carbon.permutations.map((axis) => ({\n name: axis.name.value,\n value: axis.options[axis.defaultOption]?.value ?? null\n }))\n }\n }, { source, decodeBytecode: false, decodeInstructions: false });\n}\n\n/**\n * Derives the WGSL set document for one permutation.\n *\n * `sourceMap` is absent rather than empty, and that is not an oversight: its\n * offsets index DXBC bytes the container does not carry, so it is a translator\n * diagnostic and was dropped on write. It was separately measured to suppress\n * no sharing, so keeping it would have bought nothing either.\n *\n * @param {object} container Loaded container.\n * @param {object} [options] View options.\n * @param {number} [options.permutationIndex] Permutation to describe.\n * @returns {object} WGSL set document.\n */\nexport function deriveWgsl(container, options = {})\n{\n const permutationIndex = options.permutationIndex ?? defaultPermutationIndex(container);\n const body = container.GetBackendBodyPrograms(permutationIndex);\n const shaders = [];\n const layouts = [];\n const resourceTransforms = [];\n\n for (const pass of body?.passes ?? [])\n {\n for (const shader of pass.shaders)\n {\n shaders.push({\n key: shader.key,\n techniqueName: shader.techniqueName,\n passIndex: shader.passIndex,\n stageName: shader.stageName,\n stage: shader.stage,\n stageType: shader.stageType,\n entryPoint: shader.entryPoint,\n code: shader.code,\n ...(shader.threadGroupSize ? { threadGroupSize: [ ...shader.threadGroupSize ] } : {})\n });\n }\n for (const layout of pass.layouts) layouts.push(layout);\n for (const transform of pass.resourceTransforms ?? []) resourceTransforms.push(transform);\n }\n\n return {\n format: \"CJS_WGSL_SET\",\n formatVersion: WGSL_SET_VERSION,\n shaders,\n layouts,\n ...(resourceTransforms.length ? { resourceTransforms } : {})\n };\n}\n\n/**\n * Derives the `WGSB` body-set view the chunk package used to store.\n *\n * `engine-webgpu` resolves a permutation's translated programs through this,\n * which is part of the recorded layering defect -- it should read the shader,\n * not a package document. The view keeps that path working unchanged while the\n * wire format moves underneath it, and goes away with the cleanup.\n *\n * Nothing here is stored. A body's status is whether its stages carry programs,\n * its passes are the record tree's passes, and a pass unit is that pass's\n * shaders and layouts. Bodies are keyed by distinct stored offset, which is\n * exactly what Carbon's alias dedupe produces.\n *\n * @param {object} container Loaded container.\n * @returns {object} Body-set document.\n */\nexport function deriveBackendBodySet(container)\n{\n const bodyKeys = container.bodyKeyByOffset;\n const passUnits = [];\n const bodies = [];\n const seen = new Set();\n\n for (let index = 0; index < container.carbon.records.length; index += 1)\n {\n const offset = container.carbon.records[index].offset;\n if (seen.has(offset)) continue;\n seen.add(offset);\n\n const bodyKey = bodyKeys.get(offset);\n const body = container.GetBackendBodyPrograms(index);\n\n if (!body || body.status !== \"translated\")\n {\n bodies.push({\n bodyKey,\n representativePermutationIndex: index,\n status: \"unsupported\",\n error: body?.error ?? \"body carries no translated programs\",\n passCount: 0,\n passes: []\n });\n continue;\n }\n\n const passes = [];\n for (const pass of body.passes)\n {\n const unit = {\n key: `unit${passUnits.length}`,\n // Hashed from the unit's own content. The chunk body set stored\n // this digest to detect a unit drifting from what referenced it;\n // derived from the content it identifies, it cannot.\n sha256: sha256Utf8(`${JSON.stringify({\n shaders: pass.shaders,\n layouts: pass.layouts,\n resourceTransforms: pass.resourceTransforms ?? []\n })}\n`),\n wgslSetVersion: WGSL_SET_VERSION,\n shaders: pass.shaders,\n layouts: pass.layouts,\n ...(pass.resourceTransforms?.length\n ? { resourceTransforms: pass.resourceTransforms }\n : {})\n };\n passUnits.push(unit);\n passes.push({ passKey: pass.passKey, unitKey: unit.key });\n }\n\n bodies.push({\n bodyKey,\n representativePermutationIndex: index,\n status: \"translated\",\n error: null,\n passCount: passes.length,\n passes\n });\n }\n\n return {\n format: \"CJS_WGSL_BODY_SET\",\n formatVersion: 1,\n bodyCount: bodies.length,\n translatedBodyCount: bodies.filter((body) => body.status === \"translated\").length,\n passUnitCount: passUnits.length,\n passUnits,\n bodies\n };\n}\n\n/**\n * Finds the permutation a package resolves to.\n *\n * A \"selected\" package is not a different format -- the container always carries\n * every permutation. What makes it selected is that exactly one body was\n * translated, so the resolved permutation is the first one whose body carries\n * programs. When every body is translated, there is nothing to single out and\n * Carbon's own default applies.\n *\n * @param {object} container Loaded container.\n * @returns {number} Permutation index.\n */\nexport function resolvedPermutationIndex(container)\n{\n const translated = [];\n const seen = new Set();\n\n for (let index = 0; index < container.carbon.records.length; index += 1)\n {\n const offset = container.carbon.records[index].offset;\n if (seen.has(offset)) continue;\n seen.add(offset);\n\n const body = container.GetBackendBodyPrograms(index);\n if (body?.status === \"translated\") translated.push(index);\n if (translated.length > 1) return defaultPermutationIndex(container);\n }\n\n return translated.length === 1 ? translated[0] : defaultPermutationIndex(container);\n}\n\n/**\n * Derives the `META`-shaped view the chunk package used to store.\n *\n * Every field here is recovered from the records rather than carried:\n * `selectedOptions` from the resolved permutation's option indices, and\n * `wgslSelection` from which passes and stages actually hold programs. Build-time\n * *policy* -- `bodyMode`, `sourceIdentity`, `completeness` -- is deliberately not\n * here: it describes how the artifact was produced, not what it is, and Carbon\n * stores none of it.\n *\n * @param {object} container Loaded container.\n * @param {object} [options] View options.\n * @param {string} [options.source] Source label.\n * @param {number} [options.permutationIndex] Permutation to describe.\n * @returns {object} Metadata document.\n */\nexport function deriveMetadata(container, options = {})\n{\n const source = options.source || container.sourcePath || \"memory\";\n const permutationIndex = options.permutationIndex ?? resolvedPermutationIndex(container);\n const variant = container.permutationGraph.variants[permutationIndex];\n const axes = container.carbon.permutations;\n\n const selectedOptions = axes.map((axis, axisIndex) => ({\n name: axis.name.value,\n value: axis.options[variant?.optionIndices?.[axisIndex] ?? axis.defaultOption]?.value ?? null\n }));\n\n const body = container.GetBackendBodyPrograms(permutationIndex);\n const selectedStageKeys = (body?.passes ?? [])\n .flatMap((pass) => pass.shaders.map((shader) => shader.key));\n const passKeys = (body?.passes ?? []).map((pass) => pass.passKey);\n\n return {\n effectName: source,\n sourcePath: source,\n bodyIndex: permutationIndex,\n selectedOptions,\n ...(passKeys.length === 1\n ? {\n wgslSelection: {\n mode: \"explicit\",\n completePasses: true,\n techniqueName: passKeys[0].slice(0, passKeys[0].lastIndexOf(\".pass\")),\n passIndex: Number(/\\.pass([0-9]+)$/u.exec(passKeys[0])?.[1] ?? 0),\n requestedStageNames: Array.from(new Set(\n (body?.passes ?? []).flatMap((pass) => pass.shaders.map((shader) => shader.stageName))\n )),\n selectedStageKeys\n }\n }\n : {})\n };\n}\n\n/**\n * Derives the `INFO`-shaped summary the chunk package used to store.\n *\n * Everything here is counted from the records rather than trusted from a stored\n * field, which is why no digest accompanies it: a count derived from the thing\n * it counts cannot disagree with it.\n *\n * @param {object} container Loaded container.\n * @param {object} [options] View options.\n * @param {string} [options.source] Source label.\n * @returns {object} Info document.\n */\nexport function deriveInfo(container, options = {})\n{\n const source = options.source || container.sourcePath || \"memory\";\n const graph = container.permutationGraph;\n return {\n format: \"CEWGPU\",\n packageKind: \"tr2-effect-webgpu\",\n sourcePath: source,\n effectVersion: container.carbon.version,\n compilerVersion: [ ...container.carbon.compilerVersion ],\n permutationCount: graph.variants.length,\n uniqueBodyCount: graph.bodies.length\n };\n}\n\nexport default deriveAnalysis;\n"],"names":["defaultPermutationIndex","container","multiplier","index","axis","carbon","permutations","defaultOption","options","length","records","describeRuntime","permutationIndex","source","runtimeDescriptionFromCarbon","GetDescription","effectName","version","bytecodeFor","stage","stageType","HlslShaderBytecode","stageName","hlslShaderStageName","bytes","shaderData","shaderSize","size","stringTableOffset","offset","deriveAnalysis","sourcePath","effectDescription","buildEffectAnalysis","bindingManifest","HlslEffectBindingManifest","fromEffectDescription","effectRes","m_compilerVersion","selection","bodyIndex","selectedOptions","map","name","value","decodeBytecode","decodeInstructions","deriveWgsl","body","GetBackendBodyPrograms","shaders","layouts","resourceTransforms","pass","passes","shader","push","key","techniqueName","passIndex","entryPoint","code","threadGroupSize","layout","transform","format","formatVersion","WGSL_SET_VERSION","deriveBackendBodySet","bodyKeys","bodyKeyByOffset","passUnits","bodies","seen","Set","has","add","bodyKey","get","status","representativePermutationIndex","error","passCount","unit","sha256","sha256Utf8","JSON","stringify","wgslSetVersion","passKey","unitKey","bodyCount","translatedBodyCount","filter","passUnitCount","resolvedPermutationIndex","translated","deriveMetadata","variant","permutationGraph","variants","axes","axisIndex","optionIndices","selectedStageKeys","flatMap","passKeys","wgslSelection","mode","completePasses","slice","lastIndexOf","Number","exec","requestedStageNames","Array","from","deriveInfo","graph","packageKind","effectVersion","compilerVersion","permutationCount","uniqueBodyCount"],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { WebgpuReadError } from '../errors.js';
2
+
3
+ /**
4
+ * Structural validation for a loaded WebGPU effect container.
5
+ *
6
+ * This replaces `validateEffectPackageEnvelope`, which was 1762 lines. Most of
7
+ * that is **deleted rather than translated**, and the reason matters: those
8
+ * checks were not format invariants. The chunk container shattered one logical
9
+ * tree into flat string-keyed arrays across `INFO`, `META`, `ANLS`, `WGSL`,
10
+ * `PGRF` and `WGSB`, each holding a projection of the same effect, and roughly
11
+ * 600 lines asserted that the projections still agreed with each other. A record
12
+ * layout makes that question unaskable: containment replaces reference, position
13
+ * replaces key, and the count word before an array replaces `Array.isArray`.
14
+ *
15
+ * What is deliberately **not** re-checked here, because phase 1 already enforces
16
+ * it and checking twice means two places to keep correct:
17
+ *
18
+ * - count caps, arena containment, offset-table density and positional indexing,
19
+ * and the version range — `CjsCarbonEffectReader`;
20
+ * - every sized record parsing to exactly its declared end — Rule 1, in the
21
+ * record codec and the block codec;
22
+ * - duplicate stage type within a pass, and stage-type range — the shape adapter
23
+ * fails closed on both, which is where a runtime description is built.
24
+ *
25
+ * What remains is the one thing that is genuinely this layer's: the container
26
+ * admits all six of Carbon's stage types by design, and WebGPU can express three.
27
+ * That restriction belongs to the backend, not to the container — the Carbon
28
+ * region is backend-invariant, and a container that admitted only three would not
29
+ * be Carbon's container.
30
+ */
31
+
32
+ /** Stage types WebGPU can express, by Carbon's `InputStageType` numbering. */
33
+ const WEBGPU_STAGE_TYPES = new Set([0, 1, 2]);
34
+
35
+ /**
36
+ * Validates a loaded container for WebGPU consumption.
37
+ *
38
+ * @param {object} container Loaded `CewgpuContainer`.
39
+ * @param {object} [options] Validation options.
40
+ * @param {string} [options.source] Source label for diagnostics.
41
+ * @throws {WebgpuReadError} When the container cannot serve WebGPU.
42
+ */
43
+ function validateEffectContainer(container, options = {}) {
44
+ const source = options.source || container.sourcePath || "memory";
45
+ if (!container?.IsGood()) {
46
+ throw new WebgpuReadError("CEWGPU container is not readable", {
47
+ source,
48
+ cause: container?.readError ?? null
49
+ });
50
+ }
51
+ if (!container.carbon.records.length) {
52
+ throw new WebgpuReadError("CEWGPU container declares no permutation bodies", {
53
+ source
54
+ });
55
+ }
56
+
57
+ // One check per distinct stored body. Aliased rows share a blob, so checking
58
+ // per permutation would re-check identical bytes up to twenty times over on
59
+ // the effects that alias hardest.
60
+ const checked = new Set();
61
+ for (let index = 0; index < container.carbon.records.length; index += 1) {
62
+ const offset = container.carbon.records[index].offset;
63
+ if (checked.has(offset)) continue;
64
+ checked.add(offset);
65
+ const description = container.GetDescription(index);
66
+ for (const technique of description.techniques) {
67
+ for (const [passIndex, pass] of technique.passes.entries()) {
68
+ for (const stage of pass.stages) {
69
+ // A stage carrying no program is reflection-only: the body
70
+ // is known, the translation is not, and the emitter says so
71
+ // with a zero-length payload. Those are legitimate — 107
72
+ // shipped body-passes carry a geometry stage — so the rule
73
+ // is about stages with a program, not about stages.
74
+ if (stage.shaderData.size === 0) continue;
75
+ if (!WEBGPU_STAGE_TYPES.has(stage.type)) {
76
+ throw new WebgpuReadError(`CEWGPU container carries a program for stage type ${stage.type}, ` + "which WebGPU cannot express", {
77
+ source,
78
+ permutationIndex: index,
79
+ pass: `${technique.name.value}.pass${passIndex}`,
80
+ stageType: stage.type
81
+ });
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ export { validateEffectContainer };
90
+ //# sourceMappingURL=validateContainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateContainer.js","sources":["../../../../../../src/formats/webgpu/core/cewgpu/validateContainer.js"],"sourcesContent":["import { WebgpuReadError } from \"../errors.js\";\n\n/**\n * Structural validation for a loaded WebGPU effect container.\n *\n * This replaces `validateEffectPackageEnvelope`, which was 1762 lines. Most of\n * that is **deleted rather than translated**, and the reason matters: those\n * checks were not format invariants. The chunk container shattered one logical\n * tree into flat string-keyed arrays across `INFO`, `META`, `ANLS`, `WGSL`,\n * `PGRF` and `WGSB`, each holding a projection of the same effect, and roughly\n * 600 lines asserted that the projections still agreed with each other. A record\n * layout makes that question unaskable: containment replaces reference, position\n * replaces key, and the count word before an array replaces `Array.isArray`.\n *\n * What is deliberately **not** re-checked here, because phase 1 already enforces\n * it and checking twice means two places to keep correct:\n *\n * - count caps, arena containment, offset-table density and positional indexing,\n * and the version range — `CjsCarbonEffectReader`;\n * - every sized record parsing to exactly its declared end — Rule 1, in the\n * record codec and the block codec;\n * - duplicate stage type within a pass, and stage-type range — the shape adapter\n * fails closed on both, which is where a runtime description is built.\n *\n * What remains is the one thing that is genuinely this layer's: the container\n * admits all six of Carbon's stage types by design, and WebGPU can express three.\n * That restriction belongs to the backend, not to the container — the Carbon\n * region is backend-invariant, and a container that admitted only three would not\n * be Carbon's container.\n */\n\n/** Stage types WebGPU can express, by Carbon's `InputStageType` numbering. */\nconst WEBGPU_STAGE_TYPES = new Set([ 0, 1, 2 ]);\n\n/**\n * Validates a loaded container for WebGPU consumption.\n *\n * @param {object} container Loaded `CewgpuContainer`.\n * @param {object} [options] Validation options.\n * @param {string} [options.source] Source label for diagnostics.\n * @throws {WebgpuReadError} When the container cannot serve WebGPU.\n */\nexport function validateEffectContainer(container, options = {})\n{\n const source = options.source || container.sourcePath || \"memory\";\n\n if (!container?.IsGood())\n {\n throw new WebgpuReadError(\"CEWGPU container is not readable\", {\n source,\n cause: container?.readError ?? null\n });\n }\n\n if (!container.carbon.records.length)\n {\n throw new WebgpuReadError(\"CEWGPU container declares no permutation bodies\", { source });\n }\n\n // One check per distinct stored body. Aliased rows share a blob, so checking\n // per permutation would re-check identical bytes up to twenty times over on\n // the effects that alias hardest.\n const checked = new Set();\n\n for (let index = 0; index < container.carbon.records.length; index += 1)\n {\n const offset = container.carbon.records[index].offset;\n if (checked.has(offset)) continue;\n checked.add(offset);\n\n const description = container.GetDescription(index);\n\n for (const technique of description.techniques)\n {\n for (const [ passIndex, pass ] of technique.passes.entries())\n {\n for (const stage of pass.stages)\n {\n // A stage carrying no program is reflection-only: the body\n // is known, the translation is not, and the emitter says so\n // with a zero-length payload. Those are legitimate — 107\n // shipped body-passes carry a geometry stage — so the rule\n // is about stages with a program, not about stages.\n if (stage.shaderData.size === 0) continue;\n\n if (!WEBGPU_STAGE_TYPES.has(stage.type))\n {\n throw new WebgpuReadError(\n `CEWGPU container carries a program for stage type ${stage.type}, `\n + \"which WebGPU cannot express\",\n {\n source,\n permutationIndex: index,\n pass: `${technique.name.value}.pass${passIndex}`,\n stageType: stage.type\n }\n );\n }\n }\n }\n }\n }\n}\n\nexport default validateEffectContainer;\n"],"names":["WEBGPU_STAGE_TYPES","Set","validateEffectContainer","container","options","source","sourcePath","IsGood","WebgpuReadError","cause","readError","carbon","records","length","checked","index","offset","has","add","description","GetDescription","technique","techniques","passIndex","pass","passes","entries","stage","stages","shaderData","size","type","permutationIndex","name","value","stageType"],"mappings":";;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,MAAMA,kBAAkB,GAAG,IAAIC,GAAG,CAAC,CAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAE,CAAC;;AAE/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,uBAAuBA,CAACC,SAAS,EAAEC,OAAO,GAAG,EAAE,EAC/D;EACI,MAAMC,MAAM,GAAGD,OAAO,CAACC,MAAM,IAAIF,SAAS,CAACG,UAAU,IAAI,QAAQ;AAEjE,EAAA,IAAI,CAACH,SAAS,EAAEI,MAAM,EAAE,EACxB;AACI,IAAA,MAAM,IAAIC,eAAe,CAAC,kCAAkC,EAAE;MAC1DH,MAAM;AACNI,MAAAA,KAAK,EAAEN,SAAS,EAAEO,SAAS,IAAI;AACnC,KAAC,CAAC;AACN,EAAA;EAEA,IAAI,CAACP,SAAS,CAACQ,MAAM,CAACC,OAAO,CAACC,MAAM,EACpC;AACI,IAAA,MAAM,IAAIL,eAAe,CAAC,iDAAiD,EAAE;AAAEH,MAAAA;AAAO,KAAC,CAAC;AAC5F,EAAA;;AAEA;AACA;AACA;AACA,EAAA,MAAMS,OAAO,GAAG,IAAIb,GAAG,EAAE;AAEzB,EAAA,KAAK,IAAIc,KAAK,GAAG,CAAC,EAAEA,KAAK,GAAGZ,SAAS,CAACQ,MAAM,CAACC,OAAO,CAACC,MAAM,EAAEE,KAAK,IAAI,CAAC,EACvE;IACI,MAAMC,MAAM,GAAGb,SAAS,CAACQ,MAAM,CAACC,OAAO,CAACG,KAAK,CAAC,CAACC,MAAM;AACrD,IAAA,IAAIF,OAAO,CAACG,GAAG,CAACD,MAAM,CAAC,EAAE;AACzBF,IAAAA,OAAO,CAACI,GAAG,CAACF,MAAM,CAAC;AAEnB,IAAA,MAAMG,WAAW,GAAGhB,SAAS,CAACiB,cAAc,CAACL,KAAK,CAAC;AAEnD,IAAA,KAAK,MAAMM,SAAS,IAAIF,WAAW,CAACG,UAAU,EAC9C;AACI,MAAA,KAAK,MAAM,CAAEC,SAAS,EAAEC,IAAI,CAAE,IAAIH,SAAS,CAACI,MAAM,CAACC,OAAO,EAAE,EAC5D;AACI,QAAA,KAAK,MAAMC,KAAK,IAAIH,IAAI,CAACI,MAAM,EAC/B;AACI;AACA;AACA;AACA;AACA;AACA,UAAA,IAAID,KAAK,CAACE,UAAU,CAACC,IAAI,KAAK,CAAC,EAAE;UAEjC,IAAI,CAAC9B,kBAAkB,CAACiB,GAAG,CAACU,KAAK,CAACI,IAAI,CAAC,EACvC;YACI,MAAM,IAAIvB,eAAe,CACrB,CAAA,kDAAA,EAAqDmB,KAAK,CAACI,IAAI,CAAA,EAAA,CAAI,GACjE,6BAA6B,EAC/B;cACI1B,MAAM;AACN2B,cAAAA,gBAAgB,EAAEjB,KAAK;cACvBS,IAAI,EAAE,GAAGH,SAAS,CAACY,IAAI,CAACC,KAAK,CAAA,KAAA,EAAQX,SAAS,CAAA,CAAE;cAChDY,SAAS,EAAER,KAAK,CAACI;AACrB,aACJ,CAAC;AACL,UAAA;AACJ,QAAA;AACJ,MAAA;AACJ,IAAA;AACJ,EAAA;AACJ;;;;"}
@@ -9,19 +9,20 @@ import { buildWgslSet } from './wgsl/buildWgslSet.js';
9
9
  import { buildResourceTransformPlan } from './wgsl/buildResourceTransformPlan.js';
10
10
  import { sha256Utf8, sha256Bytes } from '../../../format/effect/sha256.js';
11
11
  import { selectEffectStages } from './packageEffectSelection.js';
12
+ import { compareUtf8 } from '../../../format/compareUtf8.js';
12
13
  import { isParticleClearEffectCandidate, preflightParticleClearEffectProfile, particleClearEffectProofFor } from './wgsl/lowerParticleClearComputePrograms.js';
13
14
 
14
15
  const EFFECT_BACKEND_BODY_SET_CHUNK = "WGSB";
15
16
  const EFFECT_BACKEND_BODY_SET_FORMAT = "CJS_WGSL_BODY_SET";
16
17
  const EFFECT_BACKEND_BODY_SET_VERSION = 1;
17
18
 
18
- /**
19
- * One translation unit is exactly one pass of one body: the binding plan, the
20
- * resource-transform plan, and every stage's emitted WGSL are derived together.
21
- * Bodies that share an identical pass signature therefore share one unit.
22
- *
23
- * @param {object} pass Pass entry carrying its ordered stage records.
24
- * @returns {string} Stable signature for the pass translation unit.
19
+ /**
20
+ * One translation unit is exactly one pass of one body: the binding plan, the
21
+ * resource-transform plan, and every stage's emitted WGSL are derived together.
22
+ * Bodies that share an identical pass signature therefore share one unit.
23
+ *
24
+ * @param {object} pass Pass entry carrying its ordered stage records.
25
+ * @returns {string} Stable signature for the pass translation unit.
25
26
  */
26
27
  function passUnitSignature(pass) {
27
28
  return JSON.stringify({
@@ -33,7 +34,7 @@ function passUnitSignature(pass) {
33
34
  // An effect profile changes emission, so two otherwise identical
34
35
  // passes must not share one unit when their proofs differ.
35
36
  effectProfileProof: stage.effectProfileProof ? sha256Utf8(JSON.stringify(stage.effectProfileProof)) : null
36
- })).sort((left, right) => left.stageName.localeCompare(right.stageName))
37
+ })).sort((left, right) => compareUtf8(left.stageName, right.stageName))
37
38
  });
38
39
  }
39
40
  function collectBodyStageBytecode(effectDescription) {
@@ -156,19 +157,19 @@ function translatePassUnit(pass, programForKey, source, bindingPolicy) {
156
157
  })));
157
158
  }
158
159
 
159
- /**
160
- * Translate every unique source body into backend programs, layouts, and
161
- * resource transforms, sharing one translation unit between bodies whose pass
162
- * is byte-for-byte identical.
163
- *
160
+ /**
161
+ * Translate every unique source body into backend programs, layouts, and
162
+ * resource transforms, sharing one translation unit between bodies whose pass
163
+ * is byte-for-byte identical.
164
+ *
164
165
  * A body that cannot be lowered is retained as an explicitly unsupported
165
- * record. Its complete source reflection stays in `RFLX`, so a partial backend
166
- * never removes source truth.
167
- *
168
- * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.
169
- * @param {object} permutationGraph Validated `PGRF` document.
170
- * @param {object} [options] Source label, stage selection, and binding policy.
171
- * @returns {object} Frozen `CJS_WGSL_BODY_SET` document.
166
+ * in-memory record carrying its reason. Portable source reflection is built
167
+ * separately from the same effect; this body-set document does not store it.
168
+ *
169
+ * @param {object} effectRes Loaded version-15 `Tr2EffectRes`.
170
+ * @param {object} permutationGraph Validated `PGRF` document.
171
+ * @param {object} [options] Source label, stage selection, and binding policy.
172
+ * @returns {object} Frozen `CJS_WGSL_BODY_SET` document.
172
173
  */
173
174
  function buildEffectBackendBodySet(effectRes, permutationGraph, options = {}) {
174
175
  const source = options.source || "memory";