@carbonenginejs/runtime-resource 0.10.0 → 0.11.0
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/format/effect/effectPermutationGraph.js +257 -0
- package/dist/format/effect/effectPermutationGraph.js.map +1 -0
- package/dist/format/effect/effectReflectionPackage.js +636 -0
- package/dist/format/effect/effectReflectionPackage.js.map +1 -0
- package/dist/format/effect/sha256.js +114 -0
- package/dist/format/effect/sha256.js.map +1 -0
- package/dist/formats/dxbc/CjsDxbcFormat.js +142 -0
- package/dist/formats/dxbc/CjsDxbcFormat.js.map +1 -0
- package/dist/formats/dxbc/core/DxbcReader.js +266 -0
- package/dist/formats/dxbc/core/DxbcReader.js.map +1 -0
- package/dist/formats/dxbc/core/container.js +169 -0
- package/dist/formats/dxbc/core/container.js.map +1 -0
- package/dist/formats/dxbc/core/decoder.js +789 -0
- package/dist/formats/dxbc/core/decoder.js.map +1 -0
- package/dist/formats/dxbc/core/errors.js +19 -0
- package/dist/formats/dxbc/core/errors.js.map +1 -0
- package/dist/formats/dxbc/core/helpers.js +220 -0
- package/dist/formats/dxbc/core/helpers.js.map +1 -0
- package/dist/formats/dxbc/core/opcodes.js +45 -0
- package/dist/formats/dxbc/core/opcodes.js.map +1 -0
- package/dist/formats/dxbc/core/program.js +91 -0
- package/dist/formats/dxbc/core/program.js.map +1 -0
- package/dist/formats/dxbc/core/signature.js +172 -0
- package/dist/formats/dxbc/core/signature.js.map +1 -0
- package/dist/formats/dxbc/index.js +2 -0
- package/dist/formats/dxbc/index.js.map +1 -0
- package/dist/formats/hlsl/CjsHlslFormat.js +254 -0
- package/dist/formats/hlsl/CjsHlslFormat.js.map +1 -0
- package/dist/formats/hlsl/core/HlslBinaryUtils.js +15 -0
- package/dist/formats/hlsl/core/HlslBinaryUtils.js.map +1 -0
- package/dist/formats/hlsl/core/HlslEffectReadError.js +19 -0
- package/dist/formats/hlsl/core/HlslEffectReadError.js.map +1 -0
- package/dist/formats/hlsl/core/HlslEffectStateManager.js +130 -0
- package/dist/formats/hlsl/core/HlslEffectStateManager.js.map +1 -0
- package/dist/formats/hlsl/core/HlslReader.js +266 -0
- package/dist/formats/hlsl/core/HlslReader.js.map +1 -0
- package/dist/formats/hlsl/core/HlslRenderStateSetup.js +37 -0
- package/dist/formats/hlsl/core/HlslRenderStateSetup.js.map +1 -0
- package/dist/formats/hlsl/core/HlslResourceSetDescription.js +94 -0
- package/dist/formats/hlsl/core/HlslResourceSetDescription.js.map +1 -0
- package/dist/formats/hlsl/core/HlslShaderBytecode.js +44 -0
- package/dist/formats/hlsl/core/HlslShaderBytecode.js.map +1 -0
- package/dist/formats/hlsl/core/analysis.js +51 -0
- package/dist/formats/hlsl/core/analysis.js.map +1 -0
- package/dist/formats/hlsl/core/helpers.js +270 -0
- package/dist/formats/hlsl/core/helpers.js.map +1 -0
- package/dist/formats/hlsl/core/json.js +284 -0
- package/dist/formats/hlsl/core/json.js.map +1 -0
- package/dist/formats/hlsl/core/metadata.js +327 -0
- package/dist/formats/hlsl/core/metadata.js.map +1 -0
- package/dist/formats/hlsl/core/portableReflection.js +848 -0
- package/dist/formats/hlsl/core/portableReflection.js.map +1 -0
- package/dist/formats/hlsl/core/render-states.js +280 -0
- package/dist/formats/hlsl/core/render-states.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/HlslRenderContextEnum.js +43 -0
- package/dist/formats/hlsl/core/tr2/HlslRenderContextEnum.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js +294 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslEffectRes.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslShaderPermutation.js +33 -0
- package/dist/formats/hlsl/core/tr2/resources/HlslShaderPermutation.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js +416 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectBindingManifest.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectConstant.js +40 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectConstant.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js +742 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectDescription.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectLibrary.js +52 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectLibrary.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectParameterAnnotation.js +38 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectParameterAnnotation.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectResource.js +50 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectResource.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectStageInput.js +86 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectStageInput.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectTechnique.js +31 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslEffectTechnique.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslPass.js +42 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslPass.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslSamplerDescription.js +55 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslSamplerDescription.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslSamplerSetup.js +29 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslSamplerSetup.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslShader.js +220 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslShader.js.map +1 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslShaderOption.js +30 -0
- package/dist/formats/hlsl/core/tr2/shader/HlslShaderOption.js.map +1 -0
- package/dist/formats/hlsl/index.js +2 -0
- package/dist/formats/hlsl/index.js.map +1 -0
- package/dist/formats/hlsl/portable.js +2 -0
- package/dist/formats/hlsl/portable.js.map +1 -0
- package/dist/formats/index.js +4 -0
- package/dist/formats/index.js.map +1 -1
- package/dist/formats/webgl/CjsWebglFormat.js +221 -0
- package/dist/formats/webgl/CjsWebglFormat.js.map +1 -0
- package/dist/formats/webgl/core/cewg/CewgPackage.js +339 -0
- package/dist/formats/webgl/core/cewg/CewgPackage.js.map +1 -0
- package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js +104 -0
- package/dist/formats/webgl/core/cewg/CewgPackageBuilder.js.map +1 -0
- package/dist/formats/webgl/core/cewg/binary.js +101 -0
- package/dist/formats/webgl/core/cewg/binary.js.map +1 -0
- package/dist/formats/webgl/core/cewgCompleteness.js +81 -0
- package/dist/formats/webgl/core/cewgCompleteness.js.map +1 -0
- package/dist/formats/webgl/core/effectPackage.js +789 -0
- package/dist/formats/webgl/core/effectPackage.js.map +1 -0
- package/dist/formats/webgl/core/effectPackageValidation.js +331 -0
- package/dist/formats/webgl/core/effectPackageValidation.js.map +1 -0
- package/dist/formats/webgl/core/errors.js +27 -0
- package/dist/formats/webgl/core/errors.js.map +1 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +2678 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslHelpers.js +89 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslHelpers.js.map +1 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js +428 -0
- package/dist/formats/webgl/core/glsl/DxbcGlslOperandFormatter.js.map +1 -0
- package/dist/formats/webgl/core/helpers.js +306 -0
- package/dist/formats/webgl/core/helpers.js.map +1 -0
- package/dist/formats/webgl/index.js +2 -0
- package/dist/formats/webgl/index.js.map +1 -0
- package/dist/formats/webgpu/CjsWebgpuFormat.js +376 -0
- package/dist/formats/webgpu/CjsWebgpuFormat.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js +415 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackage.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js +100 -0
- package/dist/formats/webgpu/core/cewgpu/CewgpuPackageBuilder.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/binary.js +93 -0
- package/dist/formats/webgpu/core/cewgpu/binary.js.map +1 -0
- package/dist/formats/webgpu/core/cewgpu/tags.js +17 -0
- package/dist/formats/webgpu/core/cewgpu/tags.js.map +1 -0
- package/dist/formats/webgpu/core/effectAnalysis.js +82 -0
- package/dist/formats/webgpu/core/effectAnalysis.js.map +1 -0
- package/dist/formats/webgpu/core/effectBackendBodySet.js +305 -0
- package/dist/formats/webgpu/core/effectBackendBodySet.js.map +1 -0
- package/dist/formats/webgpu/core/effectPackageValidation.js +1078 -0
- package/dist/formats/webgpu/core/effectPackageValidation.js.map +1 -0
- package/dist/formats/webgpu/core/errors.js +20 -0
- package/dist/formats/webgpu/core/errors.js.map +1 -0
- package/dist/formats/webgpu/core/helpers.js +448 -0
- package/dist/formats/webgpu/core/helpers.js.map +1 -0
- package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js +223 -0
- package/dist/formats/webgpu/core/ir/analyzeRegisterValues.js.map +1 -0
- package/dist/formats/webgpu/core/ir/buildControlFlow.js +220 -0
- package/dist/formats/webgpu/core/ir/buildControlFlow.js.map +1 -0
- package/dist/formats/webgpu/core/ir/indexableTemps.js +137 -0
- package/dist/formats/webgpu/core/ir/indexableTemps.js.map +1 -0
- package/dist/formats/webgpu/core/ir/inferValueTypes.js +454 -0
- package/dist/formats/webgpu/core/ir/inferValueTypes.js.map +1 -0
- package/dist/formats/webgpu/core/ir/lowerDxbcToIr.js +494 -0
- package/dist/formats/webgpu/core/ir/lowerDxbcToIr.js.map +1 -0
- package/dist/formats/webgpu/core/ir/resolveRegisterFlow.js +177 -0
- package/dist/formats/webgpu/core/ir/resolveRegisterFlow.js.map +1 -0
- package/dist/formats/webgpu/core/ir/sourceLanes.js +61 -0
- package/dist/formats/webgpu/core/ir/sourceLanes.js.map +1 -0
- package/dist/formats/webgpu/core/packageEffect.js +300 -0
- package/dist/formats/webgpu/core/packageEffect.js.map +1 -0
- package/dist/formats/webgpu/core/packageEffectSelection.js +164 -0
- package/dist/formats/webgpu/core/packageEffectSelection.js.map +1 -0
- package/dist/formats/webgpu/core/packageMetadata.js +8 -0
- package/dist/formats/webgpu/core/packageMetadata.js.map +1 -0
- package/dist/formats/webgpu/core/schema.js +4 -0
- package/dist/formats/webgpu/core/schema.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js +275 -0
- package/dist/formats/webgpu/core/wgsl/buildResourceTransformPlan.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js +182 -0
- package/dist/formats/webgpu/core/wgsl/buildWgslBindingPlan.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js +324 -0
- package/dist/formats/webgpu/core/wgsl/buildWgslSet.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/emitWgsl.js +356 -0
- package/dist/formats/webgpu/core/wgsl/emitWgsl.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/hoistEscapingValues.js +77 -0
- package/dist/formats/webgpu/core/wgsl/hoistEscapingValues.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerBindingLayout.js +451 -0
- package/dist/formats/webgpu/core/wgsl/lowerBindingLayout.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js +754 -0
- package/dist/formats/webgpu/core/wgsl/lowerComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerCreateHistogramsComputeProgram.js +457 -0
- package/dist/formats/webgpu/core/wgsl/lowerCreateHistogramsComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerFragmentProgram.js +1572 -0
- package/dist/formats/webgpu/core/wgsl/lowerFragmentProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerMergeHistogramsComputeProgram.js +659 -0
- package/dist/formats/webgpu/core/wgsl/lowerMergeHistogramsComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerParticleClearComputePrograms.js +734 -0
- package/dist/formats/webgpu/core/wgsl/lowerParticleClearComputePrograms.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerParticleEmitComputeProgram.js +583 -0
- package/dist/formats/webgpu/core/wgsl/lowerParticleEmitComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerSkinVerticesComputeProgram.js +621 -0
- package/dist/formats/webgpu/core/wgsl/lowerSkinVerticesComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortComputeProgram.js +824 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortInnerComputeProgram.js +697 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortInnerComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortStepComputeProgram.js +559 -0
- package/dist/formats/webgpu/core/wgsl/lowerSortStepComputeProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/lowerVertexProgram.js +1328 -0
- package/dist/formats/webgpu/core/wgsl/lowerVertexProgram.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/particleEmitSemanticDigest.js +110 -0
- package/dist/formats/webgpu/core/wgsl/particleEmitSemanticDigest.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/precisionControls.js +55 -0
- package/dist/formats/webgpu/core/wgsl/precisionControls.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/selectionPlans.js +758 -0
- package/dist/formats/webgpu/core/wgsl/selectionPlans.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/uniformity.js +78 -0
- package/dist/formats/webgpu/core/wgsl/uniformity.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/validateExactComputeIr.js +196 -0
- package/dist/formats/webgpu/core/wgsl/validateExactComputeIr.js.map +1 -0
- package/dist/formats/webgpu/core/wgsl/validateHandleOperand.js +37 -0
- package/dist/formats/webgpu/core/wgsl/validateHandleOperand.js.map +1 -0
- package/dist/formats/webgpu/index.js +2 -0
- package/dist/formats/webgpu/index.js.map +1 -0
- package/dist/resource/shader/Tr2EffectRes.js +159 -150
- package/dist/resource/shader/Tr2EffectRes.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialArea.js +23 -28
- package/dist/resource/shader/Tr2MaterialArea.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialMesh.js +21 -27
- package/dist/resource/shader/Tr2MaterialMesh.js.map +1 -1
- package/dist/resource/shader/Tr2MaterialRes.js +24 -29
- package/dist/resource/shader/Tr2MaterialRes.js.map +1 -1
- package/dist/resource/shader/Tr2Shader.js +45 -39
- package/dist/resource/shader/Tr2Shader.js.map +1 -1
- package/dist/resource/shader/Tr2ShaderPermutation.js +30 -32
- package/dist/resource/shader/Tr2ShaderPermutation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectConstant.js +113 -78
- package/dist/resource/shader/reflection/Tr2EffectConstant.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectDefine.js +17 -23
- package/dist/resource/shader/reflection/Tr2EffectDefine.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectDescription.js +23 -29
- package/dist/resource/shader/reflection/Tr2EffectDescription.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js +44 -40
- package/dist/resource/shader/reflection/Tr2EffectLibrary.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +82 -86
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectResource.js +55 -61
- package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js +49 -45
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js +25 -30
- package/dist/resource/shader/reflection/Tr2EffectTechnique.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2Pass.js +30 -32
- package/dist/resource/shader/reflection/Tr2Pass.js.map +1 -1
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js +21 -26
- package/dist/resource/shader/sampler/Tr2SamplerSetup.js.map +1 -1
- package/docs/formats/README.md +4 -0
- package/docs/formats/provenance.md +11 -3
- package/docs/reference/classes/formats.md +440 -0
- package/format-notices/dxbc/LICENSE +21 -0
- package/format-notices/dxbc/NOTICE +20 -0
- package/format-notices/hlsl/LICENSE +21 -0
- package/format-notices/hlsl/NOTICE +25 -0
- package/format-notices/webgl/LICENSE +21 -0
- package/format-notices/webgl/NOTICE +35 -0
- package/format-notices/webgpu/LICENSE +21 -0
- package/format-notices/webgpu/NOTICE +31 -0
- package/package.json +6 -1
- package/dist/resource/shader/portableValidation.js +0 -461
- package/dist/resource/shader/portableValidation.js.map +0 -1
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import { readRaw } from './helpers.js';
|
|
2
|
+
import { isPlainObject } from '@carbonenginejs/runtime-utils/is';
|
|
3
|
+
import { HlslEffectStateManager } from './HlslEffectStateManager.js';
|
|
4
|
+
import { HlslRenderContextEnum, hlslShaderStageName } from './tr2/HlslRenderContextEnum.js';
|
|
5
|
+
import { HlslShader } from './tr2/shader/HlslShader.js';
|
|
6
|
+
|
|
7
|
+
const EFFECT_BODY_REFLECTION_FORMAT = "CJS_EFFECT_BODY_REFLECTION";
|
|
8
|
+
const EFFECT_BODY_REFLECTION_VERSION = 1;
|
|
9
|
+
const PORTABLE_EFFECT_VERSION = 15;
|
|
10
|
+
const UINT8_MAX = 0xff;
|
|
11
|
+
const UINT32_MAX = 0xffffffff;
|
|
12
|
+
const CONSTANT_BYTES_MAX = 4096;
|
|
13
|
+
const EFFECT_BODY_COUNT_MAX = 0x10000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Reads one exact permutation-table body into the versioned portable reflection
|
|
17
|
+
* contract used by backend effect packagers.
|
|
18
|
+
*
|
|
19
|
+
* @param {Uint8Array|ArrayBuffer|ArrayBufferView} input Compiled Tr2 effect bytes.
|
|
20
|
+
* @param {object} [options] Source label and exact body index.
|
|
21
|
+
* @param {string} [options.source] Diagnostic source label.
|
|
22
|
+
* @param {number} [options.permutationIndex] Exact zero-based body-table index.
|
|
23
|
+
* @returns {object} Complete portable reflection for the selected source body.
|
|
24
|
+
*/
|
|
25
|
+
function readEffectBodyReflection(input, options = {}) {
|
|
26
|
+
const source = String(options.source ?? "memory").trim() || "memory";
|
|
27
|
+
const effectRes = readRaw(input, {
|
|
28
|
+
source
|
|
29
|
+
});
|
|
30
|
+
return buildEffectBodyReflection(effectRes, options.permutationIndex ?? 0);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Serializes one already-loaded effect body without applying global or local
|
|
35
|
+
* option overrides.
|
|
36
|
+
*
|
|
37
|
+
* Returned byte arrays are owned copies. The document contains source
|
|
38
|
+
* reflection and source programs only; renderer handles and derived resource
|
|
39
|
+
* objects are deliberately excluded.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} effectRes Loaded raw HlslEffectRes graph.
|
|
42
|
+
* @param {number} permutationIndex Exact zero-based body-table index.
|
|
43
|
+
* @returns {object} Complete portable body-reflection document.
|
|
44
|
+
*/
|
|
45
|
+
function buildEffectBodyReflection(effectRes, permutationIndex) {
|
|
46
|
+
if (!effectRes || typeof effectRes !== "object" || typeof effectRes.GetShaderByIndex !== "function") {
|
|
47
|
+
throw new TypeError("Portable effect reflection requires a loaded HlslEffectRes with GetShaderByIndex");
|
|
48
|
+
}
|
|
49
|
+
assertUint(permutationIndex, "Portable effect reflection permutationIndex");
|
|
50
|
+
if (permutationIndex >= effectRes.m_offsetCount) {
|
|
51
|
+
throw new RangeError(`Portable effect reflection body index ${permutationIndex} is unavailable`);
|
|
52
|
+
}
|
|
53
|
+
if (effectRes.m_version !== PORTABLE_EFFECT_VERSION) {
|
|
54
|
+
throw new Error(`Portable effect reflection requires source effect version ${PORTABLE_EFFECT_VERSION}`);
|
|
55
|
+
}
|
|
56
|
+
const sourceRecord = normalizeBodySourceRecord(effectRes.m_offsets?.[permutationIndex], permutationIndex, effectRes.m_data?.byteLength);
|
|
57
|
+
const shader = decodeEffectBodyFresh(effectRes, sourceRecord);
|
|
58
|
+
const description = shader?.GetEffectDescription?.();
|
|
59
|
+
if (!description || description.readError) {
|
|
60
|
+
throw description?.readError || new Error(`Effect body ${permutationIndex} could not be decoded`);
|
|
61
|
+
}
|
|
62
|
+
const document = {
|
|
63
|
+
format: EFFECT_BODY_REFLECTION_FORMAT,
|
|
64
|
+
formatVersion: EFFECT_BODY_REFLECTION_VERSION,
|
|
65
|
+
mode: "single-body",
|
|
66
|
+
keyScope: "body-local",
|
|
67
|
+
coverage: {
|
|
68
|
+
bodies: "single",
|
|
69
|
+
reflection: "complete",
|
|
70
|
+
sourcePrograms: "complete",
|
|
71
|
+
constantDefaults: "exact"
|
|
72
|
+
},
|
|
73
|
+
source: {
|
|
74
|
+
label: String(effectRes.sourcePath ?? ""),
|
|
75
|
+
effectVersion: effectRes.m_version,
|
|
76
|
+
compilerVersion: effectRes.m_compilerVersion,
|
|
77
|
+
nativeHash: copyBytes(effectRes.m_hash),
|
|
78
|
+
stringTableByteLength: effectRes.m_stringTableSize,
|
|
79
|
+
byteLength: effectRes.m_data?.byteLength
|
|
80
|
+
},
|
|
81
|
+
permutationIndex,
|
|
82
|
+
sourceRecord: {
|
|
83
|
+
offset: sourceRecord.offset,
|
|
84
|
+
byteLength: sourceRecord.byteLength
|
|
85
|
+
},
|
|
86
|
+
effect: emitEffect(description)
|
|
87
|
+
};
|
|
88
|
+
validateEffectBodyReflection(document);
|
|
89
|
+
return document;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Enumerates first-occurrence-ordered groups of byte-identical effect bodies.
|
|
94
|
+
*
|
|
95
|
+
* This inspects owned source bytes without decoding bodies or touching parser
|
|
96
|
+
* caches. Distinct source ranges with identical bytes are grouped together.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} effectRes Loaded raw version-15 HlslEffectRes graph.
|
|
99
|
+
* @returns {object[]} Frozen unique-body inventory with every permutation alias.
|
|
100
|
+
*/
|
|
101
|
+
function enumerateUniqueEffectBodies(effectRes) {
|
|
102
|
+
if (!effectRes || typeof effectRes !== "object" || effectRes.m_version !== PORTABLE_EFFECT_VERSION || !(effectRes.m_data instanceof Uint8Array) || !Array.isArray(effectRes.m_offsets) || effectRes.m_offsetCount !== effectRes.m_offsets.length || !effectRes.m_offsets.length) {
|
|
103
|
+
throw new TypeError("Portable effect body inventory requires a loaded version-15 HlslEffectRes");
|
|
104
|
+
}
|
|
105
|
+
if (effectRes.m_offsets.length > EFFECT_BODY_COUNT_MAX) {
|
|
106
|
+
throw new RangeError(`Portable effect body inventory exceeds ${EFFECT_BODY_COUNT_MAX} records`);
|
|
107
|
+
}
|
|
108
|
+
const sourceRecords = effectRes.m_offsets.map((record, permutationIndex) => normalizeBodySourceRecord(record, permutationIndex, effectRes.m_data.byteLength));
|
|
109
|
+
validateDisjointBodySourceRecords(sourceRecords);
|
|
110
|
+
const groups = [];
|
|
111
|
+
const groupByRange = new Map();
|
|
112
|
+
const groupsByFingerprint = new Map();
|
|
113
|
+
for (const [permutationIndex, sourceRecord] of sourceRecords.entries()) {
|
|
114
|
+
const rangeKey = `${sourceRecord.offset}:${sourceRecord.byteLength}`;
|
|
115
|
+
let group = groupByRange.get(rangeKey);
|
|
116
|
+
if (!group) {
|
|
117
|
+
const bytes = effectRes.m_data.subarray(sourceRecord.offset, sourceRecord.offset + sourceRecord.byteLength);
|
|
118
|
+
const fingerprint = fingerprintBytes(bytes);
|
|
119
|
+
const candidates = groupsByFingerprint.get(fingerprint) ?? [];
|
|
120
|
+
group = candidates.find(candidate => bytesEqual(candidate.bytes, bytes));
|
|
121
|
+
if (!group) {
|
|
122
|
+
group = {
|
|
123
|
+
bytes,
|
|
124
|
+
permutationIndex,
|
|
125
|
+
sourceRecord,
|
|
126
|
+
variants: []
|
|
127
|
+
};
|
|
128
|
+
candidates.push(group);
|
|
129
|
+
groupsByFingerprint.set(fingerprint, candidates);
|
|
130
|
+
groups.push(group);
|
|
131
|
+
}
|
|
132
|
+
groupByRange.set(rangeKey, group);
|
|
133
|
+
}
|
|
134
|
+
group.variants.push({
|
|
135
|
+
permutationIndex,
|
|
136
|
+
sourceRecord
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return Object.freeze(groups.map(group => Object.freeze({
|
|
140
|
+
permutationIndex: group.permutationIndex,
|
|
141
|
+
sourceRecord: group.sourceRecord,
|
|
142
|
+
variants: Object.freeze(group.variants.map(variant => Object.freeze(variant)))
|
|
143
|
+
})));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Validates a portable body-reflection document independently of its source
|
|
148
|
+
* parser objects.
|
|
149
|
+
*
|
|
150
|
+
* @param {object} document Candidate reflection document.
|
|
151
|
+
* @returns {{permutationIndex:number,techniqueCount:number,passCount:number,stageCount:number,libraryCount:number,sourceProgramCount:number}}
|
|
152
|
+
* Validated structural counts.
|
|
153
|
+
*/
|
|
154
|
+
function validateEffectBodyReflection(document) {
|
|
155
|
+
requireExactKeys(document, ["format", "formatVersion", "mode", "keyScope", "coverage", "source", "permutationIndex", "sourceRecord", "effect"], "Portable effect body reflection");
|
|
156
|
+
requireExactKeys(document.coverage, ["bodies", "reflection", "sourcePrograms", "constantDefaults"], "Portable reflection coverage");
|
|
157
|
+
if (!isRecord(document) || document.format !== EFFECT_BODY_REFLECTION_FORMAT || document.formatVersion !== EFFECT_BODY_REFLECTION_VERSION || document.mode !== "single-body" || document.keyScope !== "body-local" || !isRecord(document.coverage) || document.coverage.bodies !== "single" || document.coverage.reflection !== "complete" || document.coverage.sourcePrograms !== "complete" || document.coverage.constantDefaults !== "exact") {
|
|
158
|
+
throw new Error("Portable effect body reflection schema or coverage is unsupported");
|
|
159
|
+
}
|
|
160
|
+
assertUint(document.permutationIndex, "Portable reflection permutationIndex");
|
|
161
|
+
validateSource(document.source);
|
|
162
|
+
const sourceRecord = requireRecord(document.sourceRecord, "Portable reflection sourceRecord");
|
|
163
|
+
requireExactKeys(sourceRecord, ["offset", "byteLength"], "Portable reflection sourceRecord");
|
|
164
|
+
assertUint(sourceRecord.offset, "Portable reflection sourceRecord offset");
|
|
165
|
+
assertUint(sourceRecord.byteLength, "Portable reflection sourceRecord byteLength");
|
|
166
|
+
if (!sourceRecord.byteLength || sourceRecord.offset + sourceRecord.byteLength > document.source.byteLength) {
|
|
167
|
+
throw new Error("Portable reflection sourceRecord is outside the source envelope");
|
|
168
|
+
}
|
|
169
|
+
const effect = requireRecord(document.effect, "Portable reflection effect");
|
|
170
|
+
requireExactKeys(effect, ["annotations", "annotationGroupCount", "techniqueCount", "techniques"], "Portable reflection effect");
|
|
171
|
+
if (!Array.isArray(effect.annotations) || !Array.isArray(effect.techniques) || effect.annotationGroupCount !== effect.annotations.length || effect.techniqueCount !== effect.techniques.length) {
|
|
172
|
+
throw new Error("Portable reflection effect is malformed");
|
|
173
|
+
}
|
|
174
|
+
validateAnnotationGroups(effect.annotations, "effect annotations");
|
|
175
|
+
const counts = {
|
|
176
|
+
permutationIndex: document.permutationIndex,
|
|
177
|
+
techniqueCount: effect.techniques.length,
|
|
178
|
+
passCount: 0,
|
|
179
|
+
stageCount: 0,
|
|
180
|
+
libraryCount: 0,
|
|
181
|
+
sourceProgramCount: 0
|
|
182
|
+
};
|
|
183
|
+
const techniqueKeys = new Set();
|
|
184
|
+
for (const [techniqueIndex, technique] of effect.techniques.entries()) {
|
|
185
|
+
const context = `technique ${techniqueIndex}`;
|
|
186
|
+
requireExactKeys(technique, ["key", "name", "passCount", "libraryCount", "passes", "libraries"], `Portable reflection ${context}`);
|
|
187
|
+
requireKey(technique?.key, `technique${techniqueIndex}`, context, techniqueKeys);
|
|
188
|
+
if (typeof technique.name !== "string" || !Array.isArray(technique.passes) || !Array.isArray(technique.libraries) || technique.passCount !== technique.passes.length || technique.libraryCount !== technique.libraries.length) {
|
|
189
|
+
throw new Error(`Portable reflection ${context} is malformed`);
|
|
190
|
+
}
|
|
191
|
+
const passKeys = new Set();
|
|
192
|
+
for (const [passIndex, pass] of technique.passes.entries()) {
|
|
193
|
+
const passKey = `${technique.key}.pass${passIndex}`;
|
|
194
|
+
requireExactKeys(pass, ["key", "renderStateCount", "renderStates", "stageCount", "stages"], `Portable reflection ${context} pass ${passIndex}`);
|
|
195
|
+
requireKey(pass?.key, passKey, `${context} pass ${passIndex}`, passKeys);
|
|
196
|
+
validatePass(pass, counts, document.source);
|
|
197
|
+
counts.passCount += 1;
|
|
198
|
+
}
|
|
199
|
+
const libraryKeys = new Set();
|
|
200
|
+
for (const [libraryIndex, library] of technique.libraries.entries()) {
|
|
201
|
+
const libraryKey = `${technique.key}.library${libraryIndex}`;
|
|
202
|
+
requireExactKeys(library, ["key", "payloadSize", "sourceProgram", "exportCount", "exports", "hitGroupName", "globalInput", "localInput"], `Portable reflection ${context} library ${libraryIndex}`);
|
|
203
|
+
requireKey(library?.key, libraryKey, `${context} library ${libraryIndex}`, libraryKeys);
|
|
204
|
+
validateLibrary(library, counts, document.source);
|
|
205
|
+
counts.libraryCount += 1;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return Object.freeze({
|
|
209
|
+
...counts
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function emitEffect(description) {
|
|
213
|
+
return {
|
|
214
|
+
annotations: emitAnnotationGroups(description.annotations),
|
|
215
|
+
annotationGroupCount: description.annotations?.size ?? 0,
|
|
216
|
+
techniqueCount: description.techniques?.length ?? 0,
|
|
217
|
+
techniques: (description.techniques || []).map((technique, techniqueIndex) => emitTechnique(technique, techniqueIndex))
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function emitTechnique(technique, techniqueIndex) {
|
|
221
|
+
const key = `technique${techniqueIndex}`;
|
|
222
|
+
return {
|
|
223
|
+
key,
|
|
224
|
+
name: String(technique.name ?? ""),
|
|
225
|
+
passCount: technique.passes?.length ?? 0,
|
|
226
|
+
libraryCount: technique.libraries?.length ?? 0,
|
|
227
|
+
passes: (technique.passes || []).map((pass, passIndex) => emitPass(pass, key, passIndex)),
|
|
228
|
+
libraries: (technique.libraries || []).map((library, libraryIndex) => emitLibrary(library, key, libraryIndex))
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function emitPass(pass, techniqueKey, passIndex) {
|
|
232
|
+
const key = `${techniqueKey}.pass${passIndex}`;
|
|
233
|
+
const stages = [];
|
|
234
|
+
for (let stageType = 0; stageType < HlslRenderContextEnum.SHADER_TYPE_COUNT; stageType += 1) {
|
|
235
|
+
const input = pass.stageInputs?.[stageType];
|
|
236
|
+
if (!input?.m_exists) continue;
|
|
237
|
+
stages.push(emitStage(input, key, stageType));
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
key,
|
|
241
|
+
renderStateCount: pass.cjsRenderStateSetup?.entries?.length ?? 0,
|
|
242
|
+
renderStates: (pass.cjsRenderStateSetup?.entries || []).map(entry => ({
|
|
243
|
+
state: entry.key >>> 0,
|
|
244
|
+
value: entry.value >>> 0
|
|
245
|
+
})),
|
|
246
|
+
stageCount: stages.length,
|
|
247
|
+
stages
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function emitStage(input, passKey, stageType) {
|
|
251
|
+
return {
|
|
252
|
+
key: `${passKey}.stage${stageType}`,
|
|
253
|
+
stageType,
|
|
254
|
+
stageName: hlslShaderStageName(stageType),
|
|
255
|
+
sourceProgram: emitSourceProgram(input.cjsShaderBytecode, "stage"),
|
|
256
|
+
input: emitInput(input)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function emitLibrary(library, techniqueKey, libraryIndex) {
|
|
260
|
+
return {
|
|
261
|
+
key: `${techniqueKey}.library${libraryIndex}`,
|
|
262
|
+
payloadSize: library.payloadSize >>> 0,
|
|
263
|
+
sourceProgram: emitSourceProgram(library.cjsShaderBytecode, "library"),
|
|
264
|
+
exportCount: library.exports?.length ?? 0,
|
|
265
|
+
exports: (library.exports || []).map(entry => ({
|
|
266
|
+
type: entry.type,
|
|
267
|
+
name: String(entry.name ?? "")
|
|
268
|
+
})),
|
|
269
|
+
hitGroupName: String(library.hitGroupName ?? ""),
|
|
270
|
+
globalInput: emitInput(library.globalInput),
|
|
271
|
+
localInput: emitInput(library.localInput)
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function emitSourceProgram(bytecode, kind) {
|
|
275
|
+
if (!bytecode) {
|
|
276
|
+
throw new Error("Portable reflection source program is missing");
|
|
277
|
+
}
|
|
278
|
+
const program = {
|
|
279
|
+
kind,
|
|
280
|
+
shaderSize: bytecode.shaderSize,
|
|
281
|
+
stringTableOffset: bytecode.stringTableOffset,
|
|
282
|
+
bytes: copyBytes(bytecode.bytes)
|
|
283
|
+
};
|
|
284
|
+
if (kind === "stage") {
|
|
285
|
+
program.stageType = bytecode.stageType;
|
|
286
|
+
program.stageName = String(bytecode.stageName ?? "");
|
|
287
|
+
}
|
|
288
|
+
return program;
|
|
289
|
+
}
|
|
290
|
+
function emitInput(input) {
|
|
291
|
+
const sourceConstantValues = copyBytes(input?.sourceConstantValues);
|
|
292
|
+
const sourceConstantValueSize = input?.sourceConstantValueSize ?? 0;
|
|
293
|
+
if (sourceConstantValues.byteLength !== sourceConstantValueSize) {
|
|
294
|
+
throw new Error("Portable reflection source constant defaults are incomplete");
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
constantDefaults: {
|
|
298
|
+
declaredByteLength: sourceConstantValueSize,
|
|
299
|
+
bytes: sourceConstantValues
|
|
300
|
+
},
|
|
301
|
+
constantCount: input?.constants?.length ?? 0,
|
|
302
|
+
constants: (input?.constants || []).map(constant => ({
|
|
303
|
+
name: String(constant.name ?? ""),
|
|
304
|
+
offset: constant.offset,
|
|
305
|
+
size: constant.size,
|
|
306
|
+
type: constant.type,
|
|
307
|
+
dimension: constant.dimension,
|
|
308
|
+
elements: constant.elements,
|
|
309
|
+
isSRGB: !!constant.isSRGB,
|
|
310
|
+
isAutoregister: !!constant.isAutoregister
|
|
311
|
+
})),
|
|
312
|
+
resourceCount: input?.resources?.size ?? 0,
|
|
313
|
+
resources: emitResourceMap(input?.resources),
|
|
314
|
+
uavCount: input?.uavs?.size ?? 0,
|
|
315
|
+
uavs: emitResourceMap(input?.uavs),
|
|
316
|
+
samplerCount: input?.samplers?.size ?? 0,
|
|
317
|
+
samplers: emitSamplerMap(input?.samplers),
|
|
318
|
+
annotationCount: input?.annotation?.length ?? 0,
|
|
319
|
+
annotations: emitAnnotations(input?.annotation),
|
|
320
|
+
signature: emitSignature(input?.signature)
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function emitResourceMap(map) {
|
|
324
|
+
return Array.from(map || [], ([registerIndex, resource]) => ({
|
|
325
|
+
registerIndex,
|
|
326
|
+
name: String(resource.name ?? ""),
|
|
327
|
+
type: resource.type,
|
|
328
|
+
arrayElements: resource.arrayElements,
|
|
329
|
+
isSRGB: !!resource.isSRGB,
|
|
330
|
+
isAutoregister: !!resource.isAutoregister
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
function emitSamplerMap(map) {
|
|
334
|
+
return Array.from(map || [], ([registerIndex, setup]) => ({
|
|
335
|
+
registerIndex,
|
|
336
|
+
name: setup.name === null || setup.name === undefined ? null : String(setup.name),
|
|
337
|
+
isDynamic: !!setup.sampler?.isDynamic,
|
|
338
|
+
descriptor: emitDynamicSampler(setup.sampler)
|
|
339
|
+
}));
|
|
340
|
+
}
|
|
341
|
+
function emitDynamicSampler(sampler = {}) {
|
|
342
|
+
return {
|
|
343
|
+
comparison: !!sampler.comparison,
|
|
344
|
+
minFilter: sampler.minFilter,
|
|
345
|
+
magFilter: sampler.magFilter,
|
|
346
|
+
mipFilter: sampler.mipFilter,
|
|
347
|
+
addressU: sampler.addressU,
|
|
348
|
+
addressV: sampler.addressV,
|
|
349
|
+
addressW: sampler.addressW,
|
|
350
|
+
mipLODBiasRaw: sampler.mipLODBiasRaw,
|
|
351
|
+
maxAnisotropy: sampler.maxAnisotropy,
|
|
352
|
+
comparisonFunc: sampler.comparisonFunc,
|
|
353
|
+
borderColorRaw: (sampler.borderColorRaw || []).slice(),
|
|
354
|
+
minLODRaw: sampler.minLODRaw,
|
|
355
|
+
maxLODRaw: sampler.maxLODRaw
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function emitStaticSampler(entry) {
|
|
359
|
+
const sampler = entry.sampler || {};
|
|
360
|
+
return {
|
|
361
|
+
registerIndex: entry.registerIndex,
|
|
362
|
+
registerSpace: entry.registerSpace,
|
|
363
|
+
descriptor: {
|
|
364
|
+
comparison: !!sampler.comparison,
|
|
365
|
+
minFilter: sampler.minFilter,
|
|
366
|
+
magFilter: sampler.magFilter,
|
|
367
|
+
mipFilter: sampler.mipFilter,
|
|
368
|
+
addressU: sampler.addressU,
|
|
369
|
+
addressV: sampler.addressV,
|
|
370
|
+
addressW: sampler.addressW,
|
|
371
|
+
mipLODBiasRaw: sampler.mipLODBiasRaw,
|
|
372
|
+
maxAnisotropy: sampler.maxAnisotropy,
|
|
373
|
+
comparisonFunc: sampler.comparisonFunc,
|
|
374
|
+
borderColor: sampler.borderColor,
|
|
375
|
+
minLODRaw: sampler.minLODRaw,
|
|
376
|
+
maxLODRaw: sampler.maxLODRaw
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function emitSignature(signature = {}) {
|
|
381
|
+
return {
|
|
382
|
+
pipelineInputCount: signature.pipelineInputs?.length ?? 0,
|
|
383
|
+
pipelineInputs: (signature.pipelineInputs || []).map(entry => ({
|
|
384
|
+
usage: entry.usage,
|
|
385
|
+
registerIndex: entry.registerIndex,
|
|
386
|
+
usageIndex: entry.usageIndex,
|
|
387
|
+
usedMask: entry.usedMask,
|
|
388
|
+
type: entry.type,
|
|
389
|
+
dimension: entry.dimension
|
|
390
|
+
})),
|
|
391
|
+
registerCount: signature.registers?.length ?? 0,
|
|
392
|
+
registers: (signature.registers || []).map(entry => ({
|
|
393
|
+
registerType: entry.registerType,
|
|
394
|
+
registerIndex: entry.registerIndex,
|
|
395
|
+
arrayCount: entry.arrayCount,
|
|
396
|
+
registerCount: entry.registerCount,
|
|
397
|
+
registerSpace: entry.registerSpace
|
|
398
|
+
})),
|
|
399
|
+
staticSamplerCount: signature.samplers?.length ?? 0,
|
|
400
|
+
staticSamplers: (signature.samplers || []).map(emitStaticSampler),
|
|
401
|
+
threadGroupSize: {
|
|
402
|
+
x: signature.threadGroupSize?.x ?? 0,
|
|
403
|
+
y: signature.threadGroupSize?.y ?? 0,
|
|
404
|
+
z: signature.threadGroupSize?.z ?? 0
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function emitAnnotationGroups(map) {
|
|
409
|
+
return Array.from(map || [], ([parameterName, annotations]) => ({
|
|
410
|
+
parameterName: String(parameterName),
|
|
411
|
+
annotations: emitAnnotations(annotations)
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
function emitAnnotations(annotations) {
|
|
415
|
+
return (annotations || []).map(annotation => {
|
|
416
|
+
const record = {
|
|
417
|
+
name: String(annotation.name ?? ""),
|
|
418
|
+
type: annotation.type
|
|
419
|
+
};
|
|
420
|
+
if (annotation.type === 3) {
|
|
421
|
+
record.stringValue = String(annotation.stringValue ?? "");
|
|
422
|
+
} else {
|
|
423
|
+
record.rawValue = annotation.rawValue >>> 0;
|
|
424
|
+
}
|
|
425
|
+
return record;
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
function validateSource(source) {
|
|
429
|
+
requireRecord(source, "Portable reflection source");
|
|
430
|
+
requireExactKeys(source, ["label", "effectVersion", "compilerVersion", "nativeHash", "stringTableByteLength", "byteLength"], "Portable reflection source");
|
|
431
|
+
if (typeof source.label !== "string") {
|
|
432
|
+
throw new Error("Portable reflection source label is malformed");
|
|
433
|
+
}
|
|
434
|
+
assertUint(source.effectVersion, "Portable reflection effectVersion");
|
|
435
|
+
assertUint(source.stringTableByteLength, "Portable reflection stringTableByteLength");
|
|
436
|
+
assertUint(source.byteLength, "Portable reflection source byteLength");
|
|
437
|
+
if (!source.byteLength || source.stringTableByteLength > source.byteLength) {
|
|
438
|
+
throw new Error("Portable reflection source byteLength must be positive");
|
|
439
|
+
}
|
|
440
|
+
if (source.effectVersion !== PORTABLE_EFFECT_VERSION) {
|
|
441
|
+
throw new Error("Portable reflection effectVersion is unsupported");
|
|
442
|
+
}
|
|
443
|
+
if (source.compilerVersion !== null) {
|
|
444
|
+
assertUint(source.compilerVersion, "Portable reflection compilerVersion");
|
|
445
|
+
}
|
|
446
|
+
const hash = requireBytes(source.nativeHash, "Portable reflection nativeHash");
|
|
447
|
+
if (source.compilerVersion === null || hash.byteLength !== 32) {
|
|
448
|
+
throw new Error("Portable reflection v15 source identity is incomplete");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function validatePass(pass, counts, source) {
|
|
452
|
+
if (!Array.isArray(pass.renderStates) || !Array.isArray(pass.stages) || pass.renderStateCount !== pass.renderStates.length || pass.stageCount !== pass.stages.length) {
|
|
453
|
+
throw new Error(`Portable reflection pass ${pass.key} is malformed`);
|
|
454
|
+
}
|
|
455
|
+
validateRegisterIndexed(pass.renderStates, "state", `pass ${pass.key} render states`, entry => {
|
|
456
|
+
requireExactKeys(entry, ["state", "value"], `Portable reflection pass ${pass.key} render state`);
|
|
457
|
+
assertUint(entry.value, `pass ${pass.key} render-state value`);
|
|
458
|
+
});
|
|
459
|
+
const stageTypes = new Set();
|
|
460
|
+
const stageKeys = new Set();
|
|
461
|
+
for (const stage of pass.stages) {
|
|
462
|
+
requireExactKeys(stage, ["key", "stageType", "stageName", "sourceProgram", "input"], `Portable reflection pass ${pass.key} stage`);
|
|
463
|
+
assertUint(stage?.stageType, `pass ${pass.key} stage type`);
|
|
464
|
+
if (stage.stageType >= HlslRenderContextEnum.SHADER_TYPE_COUNT || stageTypes.has(stage.stageType)) {
|
|
465
|
+
throw new Error(`Portable reflection pass ${pass.key} stage type is invalid or duplicated`);
|
|
466
|
+
}
|
|
467
|
+
stageTypes.add(stage.stageType);
|
|
468
|
+
requireKey(stage.key, `${pass.key}.stage${stage.stageType}`, `pass ${pass.key} stage`, stageKeys);
|
|
469
|
+
if (stage.stageName !== hlslShaderStageName(stage.stageType)) {
|
|
470
|
+
throw new Error(`Portable reflection stage ${stage.key} name disagrees`);
|
|
471
|
+
}
|
|
472
|
+
validateSourceProgram(stage.sourceProgram, "stage", stage.stageType, stage.stageName, source.stringTableByteLength);
|
|
473
|
+
validateInput(stage.input, stage.key);
|
|
474
|
+
counts.stageCount += 1;
|
|
475
|
+
counts.sourceProgramCount += 1;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function validateLibrary(library, counts, source) {
|
|
479
|
+
assertUint(library.payloadSize, `library ${library.key} payloadSize`);
|
|
480
|
+
validateSourceProgram(library.sourceProgram, "library", null, null, source.stringTableByteLength);
|
|
481
|
+
if (!Array.isArray(library.exports) || library.exportCount !== library.exports.length || typeof library.hitGroupName !== "string") {
|
|
482
|
+
throw new Error(`Portable reflection library ${library.key} is malformed`);
|
|
483
|
+
}
|
|
484
|
+
const exportIdentities = new Set();
|
|
485
|
+
for (const [index, entry] of library.exports.entries()) {
|
|
486
|
+
requireExactKeys(entry, ["type", "name"], `Portable reflection library ${library.key} export ${index}`);
|
|
487
|
+
assertUint8(entry?.type, `library ${library.key} export ${index} type`);
|
|
488
|
+
const identity = `${entry.type}:${entry.name}`;
|
|
489
|
+
if (entry.type > 4 || typeof entry.name !== "string" || !entry.name || exportIdentities.has(identity)) {
|
|
490
|
+
throw new Error(`Portable reflection library ${library.key} export ${index} is malformed or duplicated`);
|
|
491
|
+
}
|
|
492
|
+
exportIdentities.add(identity);
|
|
493
|
+
}
|
|
494
|
+
validateInput(library.globalInput, `${library.key}.globalInput`);
|
|
495
|
+
validateInput(library.localInput, `${library.key}.localInput`);
|
|
496
|
+
counts.sourceProgramCount += 1;
|
|
497
|
+
}
|
|
498
|
+
function validateSourceProgram(program, expectedKind, stageType, stageName, stringTableByteLength) {
|
|
499
|
+
requireRecord(program, "Portable reflection source program");
|
|
500
|
+
requireExactKeys(program, expectedKind === "stage" ? ["kind", "stageType", "stageName", "shaderSize", "stringTableOffset", "bytes"] : ["kind", "shaderSize", "stringTableOffset", "bytes"], "Portable reflection source program");
|
|
501
|
+
if (program.kind !== expectedKind) {
|
|
502
|
+
throw new Error("Portable reflection source program kind disagrees");
|
|
503
|
+
}
|
|
504
|
+
if (expectedKind === "stage" && (program.stageType !== stageType || program.stageName !== stageName)) {
|
|
505
|
+
throw new Error("Portable reflection source program stage identity disagrees");
|
|
506
|
+
}
|
|
507
|
+
assertUint(program.shaderSize, "Portable reflection source program shaderSize");
|
|
508
|
+
const bytes = requireBytes(program.bytes, "Portable reflection source program bytes");
|
|
509
|
+
if (!bytes.byteLength || program.shaderSize !== bytes.byteLength) {
|
|
510
|
+
throw new Error("Portable reflection source program bytes are incomplete");
|
|
511
|
+
}
|
|
512
|
+
assertUint(program.stringTableOffset, "Portable reflection source program stringTableOffset");
|
|
513
|
+
if (program.stringTableOffset + program.shaderSize > stringTableByteLength) {
|
|
514
|
+
throw new Error("Portable reflection source program is outside the shared string table");
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function validateInput(input, context) {
|
|
518
|
+
requireRecord(input, `Portable reflection ${context} input`);
|
|
519
|
+
requireExactKeys(input, ["constantDefaults", "constantCount", "constants", "resourceCount", "resources", "uavCount", "uavs", "samplerCount", "samplers", "annotationCount", "annotations", "signature"], `Portable reflection ${context} input`);
|
|
520
|
+
const defaults = requireRecord(input.constantDefaults, `Portable reflection ${context} constant defaults`);
|
|
521
|
+
requireExactKeys(defaults, ["declaredByteLength", "bytes"], `Portable reflection ${context} constant defaults`);
|
|
522
|
+
assertUint(defaults.declaredByteLength, `Portable reflection ${context} constant-default length`);
|
|
523
|
+
const defaultBytes = requireBytes(defaults.bytes, `Portable reflection ${context} constant-default bytes`);
|
|
524
|
+
if (defaults.declaredByteLength !== defaultBytes.byteLength || defaultBytes.byteLength > CONSTANT_BYTES_MAX) {
|
|
525
|
+
throw new Error(`Portable reflection ${context} constant defaults are invalid`);
|
|
526
|
+
}
|
|
527
|
+
if (!Array.isArray(input.constants) || !Array.isArray(input.resources) || !Array.isArray(input.uavs) || !Array.isArray(input.samplers) || !Array.isArray(input.annotations) || input.constantCount !== input.constants.length || input.resourceCount !== input.resources.length || input.uavCount !== input.uavs.length || input.samplerCount !== input.samplers.length || input.annotationCount !== input.annotations.length) {
|
|
528
|
+
throw new Error(`Portable reflection ${context} input collections are malformed`);
|
|
529
|
+
}
|
|
530
|
+
const constantNames = new Set();
|
|
531
|
+
for (const [index, constant] of input.constants.entries()) {
|
|
532
|
+
requireExactKeys(constant, ["name", "offset", "size", "type", "dimension", "elements", "isSRGB", "isAutoregister"], `Portable reflection ${context} constant ${index}`);
|
|
533
|
+
if (!isRecord(constant) || typeof constant.name !== "string" || !constant.name || constantNames.has(constant.name)) {
|
|
534
|
+
throw new Error(`Portable reflection ${context} constant ${index} is malformed or duplicated`);
|
|
535
|
+
}
|
|
536
|
+
constantNames.add(constant.name);
|
|
537
|
+
assertUint(constant.offset, `${context} constant ${constant.name} offset`);
|
|
538
|
+
assertUint(constant.size, `${context} constant ${constant.name} size`);
|
|
539
|
+
assertUint8(constant.type, `${context} constant ${constant.name} type`);
|
|
540
|
+
assertUint8(constant.dimension, `${context} constant ${constant.name} dimension`);
|
|
541
|
+
assertUint(constant.elements, `${context} constant ${constant.name} elements`);
|
|
542
|
+
if (!constant.size || constant.type > 4 || constant.dimension < 1 || constant.offset + constant.size > CONSTANT_BYTES_MAX || typeof constant.isSRGB !== "boolean" || typeof constant.isAutoregister !== "boolean") {
|
|
543
|
+
throw new Error(`Portable reflection ${context} constant ${constant.name} is invalid`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
validateResourceList(input.resources, `${context} resources`);
|
|
547
|
+
validateResourceList(input.uavs, `${context} UAVs`);
|
|
548
|
+
validateSamplerList(input.samplers, `${context} samplers`);
|
|
549
|
+
validateAnnotations(input.annotations, `${context} annotations`);
|
|
550
|
+
validateSignature(input.signature, context);
|
|
551
|
+
validateMapSignatureReconciliation(input, context);
|
|
552
|
+
}
|
|
553
|
+
function validateResourceList(entries, context) {
|
|
554
|
+
validateRegisterIndexed(entries, "registerIndex", context, entry => {
|
|
555
|
+
requireExactKeys(entry, ["registerIndex", "name", "type", "arrayElements", "isSRGB", "isAutoregister"], `Portable reflection ${context} entry`);
|
|
556
|
+
assertUint8(entry.registerIndex, `${context} registerIndex`);
|
|
557
|
+
if (typeof entry.name !== "string" || !entry.name) {
|
|
558
|
+
throw new Error(`Portable reflection ${context} name is malformed`);
|
|
559
|
+
}
|
|
560
|
+
assertUint8(entry.type, `${context} type`);
|
|
561
|
+
assertUint(entry.arrayElements, `${context} arrayElements`);
|
|
562
|
+
if (typeof entry.isSRGB !== "boolean" || typeof entry.isAutoregister !== "boolean") {
|
|
563
|
+
throw new Error(`Portable reflection ${context} entry is malformed`);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
function validateSamplerList(entries, context) {
|
|
568
|
+
validateRegisterIndexed(entries, "registerIndex", context, entry => {
|
|
569
|
+
requireExactKeys(entry, ["registerIndex", "name", "isDynamic", "descriptor"], `Portable reflection ${context} entry`);
|
|
570
|
+
assertUint8(entry.registerIndex, `${context} registerIndex`);
|
|
571
|
+
if (entry.isDynamic && typeof entry.name !== "string" || !entry.isDynamic && entry.name !== null || typeof entry.isDynamic !== "boolean") {
|
|
572
|
+
throw new Error(`Portable reflection ${context} entry is malformed`);
|
|
573
|
+
}
|
|
574
|
+
validateSamplerDescriptor(entry.descriptor, context, false);
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
function validateSignature(signature, context) {
|
|
578
|
+
requireRecord(signature, `Portable reflection ${context} signature`);
|
|
579
|
+
requireExactKeys(signature, ["pipelineInputCount", "pipelineInputs", "registerCount", "registers", "staticSamplerCount", "staticSamplers", "threadGroupSize"], `Portable reflection ${context} signature`);
|
|
580
|
+
if (!Array.isArray(signature.pipelineInputs) || !Array.isArray(signature.registers) || !Array.isArray(signature.staticSamplers) || signature.pipelineInputCount !== signature.pipelineInputs.length || signature.registerCount !== signature.registers.length || signature.staticSamplerCount !== signature.staticSamplers.length) {
|
|
581
|
+
throw new Error(`Portable reflection ${context} signature is malformed`);
|
|
582
|
+
}
|
|
583
|
+
for (const [index, input] of signature.pipelineInputs.entries()) {
|
|
584
|
+
requireExactKeys(input, ["usage", "registerIndex", "usageIndex", "usedMask", "type", "dimension"], `Portable reflection ${context} pipeline input ${index}`);
|
|
585
|
+
for (const field of ["usage", "registerIndex", "usageIndex", "usedMask", "type", "dimension"]) {
|
|
586
|
+
assertUint8(input?.[field], `${context} pipeline input ${index} ${field}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const registerRanges = new Map();
|
|
590
|
+
for (const [index, register] of signature.registers.entries()) {
|
|
591
|
+
requireExactKeys(register, ["registerType", "registerIndex", "arrayCount", "registerCount", "registerSpace"], `Portable reflection ${context} register ${index}`);
|
|
592
|
+
assertUint8(register?.registerType, `${context} register ${index} type`);
|
|
593
|
+
assertUint(register.registerIndex, `${context} register ${index} index`);
|
|
594
|
+
assertUint(register.arrayCount, `${context} register ${index} arrayCount`);
|
|
595
|
+
assertUint(register.registerCount, `${context} register ${index} registerCount`);
|
|
596
|
+
assertUint8(register.registerSpace, `${context} register ${index} space`);
|
|
597
|
+
const classification = registerClass(register.registerType);
|
|
598
|
+
const isUnbounded = register.arrayCount === 0 && register.registerCount === 0;
|
|
599
|
+
const rangeEnd = isUnbounded ? UINT32_MAX + 1 : register.registerIndex + register.registerCount;
|
|
600
|
+
if (register.registerCount !== register.arrayCount || isUnbounded && classification !== "sampler" && classification !== "resource" && classification !== "uav" || rangeEnd > UINT32_MAX + 1) {
|
|
601
|
+
throw new Error(`Portable reflection ${context} register ${index} is malformed`);
|
|
602
|
+
}
|
|
603
|
+
const rangeKey = `${classification}:${register.registerSpace}`;
|
|
604
|
+
const ranges = registerRanges.get(rangeKey) || [];
|
|
605
|
+
if (ranges.some(([start, end]) => register.registerIndex < end && rangeEnd > start)) {
|
|
606
|
+
throw new Error(`Portable reflection ${context} signature register range overlaps`);
|
|
607
|
+
}
|
|
608
|
+
ranges.push([register.registerIndex, rangeEnd]);
|
|
609
|
+
registerRanges.set(rangeKey, ranges);
|
|
610
|
+
}
|
|
611
|
+
validateRegisterIndexed(signature.staticSamplers, "registerIndex", `${context} static samplers`, entry => {
|
|
612
|
+
requireExactKeys(entry, ["registerIndex", "registerSpace", "descriptor"], `Portable reflection ${context} static sampler`);
|
|
613
|
+
assertUint8(entry.registerSpace, `${context} static sampler space`);
|
|
614
|
+
validateSamplerDescriptor(entry.descriptor, context, true);
|
|
615
|
+
}, entry => `${entry.registerSpace}:${entry.registerIndex}`);
|
|
616
|
+
const group = requireRecord(signature.threadGroupSize, `Portable reflection ${context} threadGroupSize`);
|
|
617
|
+
requireExactKeys(group, ["x", "y", "z"], `Portable reflection ${context} threadGroupSize`);
|
|
618
|
+
assertUint(group.x, `${context} threadGroupSize.x`);
|
|
619
|
+
assertUint(group.y, `${context} threadGroupSize.y`);
|
|
620
|
+
assertUint(group.z, `${context} threadGroupSize.z`);
|
|
621
|
+
}
|
|
622
|
+
function validateMapSignatureReconciliation(input, context) {
|
|
623
|
+
const signature = input.signature;
|
|
624
|
+
validateMappedBindings(input.resources, signature.registers.filter(entry => registerClass(entry.registerType) === "resource"), `${context} resources`);
|
|
625
|
+
validateMappedBindings(input.uavs, signature.registers.filter(entry => registerClass(entry.registerType) === "uav"), `${context} UAVs`);
|
|
626
|
+
for (const sampler of input.samplers) {
|
|
627
|
+
const matches = sampler.isDynamic ? signature.registers.filter(entry => registerClass(entry.registerType) === "sampler" && entry.registerIndex === sampler.registerIndex) : signature.staticSamplers.filter(entry => entry.registerIndex === sampler.registerIndex);
|
|
628
|
+
if (matches.length > 1 || matches.length === 1 && sampler.isDynamic && matches[0].arrayCount !== 0 && matches[0].arrayCount !== 1) {
|
|
629
|
+
throw new Error(`Portable reflection ${context} sampler map disagrees with its signature`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function validateMappedBindings(entries, registers, context) {
|
|
634
|
+
for (const entry of entries) {
|
|
635
|
+
const matches = registers.filter(register => register.registerIndex === entry.registerIndex);
|
|
636
|
+
if (matches.length !== 1 || matches[0].arrayCount !== entry.arrayElements) {
|
|
637
|
+
throw new Error(`Portable reflection ${context} map disagrees with its signature`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function registerClass(registerType) {
|
|
642
|
+
if (registerType === 0) return "constantBuffer";
|
|
643
|
+
if (registerType === 1) return "sampler";
|
|
644
|
+
if (registerType >= 32 && registerType <= 63) return "resource";
|
|
645
|
+
if (registerType >= 64 && registerType <= 95) return "uav";
|
|
646
|
+
return `raw${registerType}`;
|
|
647
|
+
}
|
|
648
|
+
function validateSamplerDescriptor(descriptor, context, staticSampler) {
|
|
649
|
+
requireRecord(descriptor, `Portable reflection ${context} sampler descriptor`);
|
|
650
|
+
requireExactKeys(descriptor, ["comparison", "minFilter", "magFilter", "mipFilter", "addressU", "addressV", "addressW", "mipLODBiasRaw", "maxAnisotropy", "comparisonFunc", staticSampler ? "borderColor" : "borderColorRaw", "minLODRaw", "maxLODRaw"], `Portable reflection ${context} sampler descriptor`);
|
|
651
|
+
if (typeof descriptor.comparison !== "boolean") {
|
|
652
|
+
throw new Error(`Portable reflection ${context} sampler comparison is malformed`);
|
|
653
|
+
}
|
|
654
|
+
for (const field of ["minFilter", "magFilter", "mipFilter", "addressU", "addressV", "addressW", "maxAnisotropy", "comparisonFunc"]) {
|
|
655
|
+
assertUint8(descriptor[field], `${context} sampler ${field}`);
|
|
656
|
+
}
|
|
657
|
+
for (const field of ["mipLODBiasRaw", "minLODRaw", "maxLODRaw"]) {
|
|
658
|
+
assertUint(descriptor[field], `${context} sampler ${field}`);
|
|
659
|
+
}
|
|
660
|
+
if (staticSampler) {
|
|
661
|
+
assertUint8(descriptor.borderColor, `${context} static sampler borderColor`);
|
|
662
|
+
} else if (!Array.isArray(descriptor.borderColorRaw) || descriptor.borderColorRaw.length !== 4) {
|
|
663
|
+
throw new Error(`Portable reflection ${context} sampler borderColorRaw is malformed`);
|
|
664
|
+
} else {
|
|
665
|
+
descriptor.borderColorRaw.forEach((value, index) => assertUint(value, `${context} sampler borderColorRaw[${index}]`));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function validateAnnotationGroups(groups, context) {
|
|
669
|
+
const names = new Set();
|
|
670
|
+
for (const [index, group] of groups.entries()) {
|
|
671
|
+
requireExactKeys(group, ["parameterName", "annotations"], `Portable reflection ${context} group ${index}`);
|
|
672
|
+
if (!isRecord(group) || typeof group.parameterName !== "string" || names.has(group.parameterName) || !Array.isArray(group.annotations)) {
|
|
673
|
+
throw new Error(`Portable reflection ${context} group ${index} is malformed or duplicated`);
|
|
674
|
+
}
|
|
675
|
+
names.add(group.parameterName);
|
|
676
|
+
validateAnnotations(group.annotations, `${context} ${group.parameterName}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function validateAnnotations(annotations, context) {
|
|
680
|
+
for (const [index, annotation] of annotations.entries()) {
|
|
681
|
+
requireExactKeys(annotation, annotation?.type === 3 ? ["name", "type", "stringValue"] : ["name", "type", "rawValue"], `Portable reflection ${context} annotation ${index}`);
|
|
682
|
+
if (!isRecord(annotation) || typeof annotation.name !== "string" || !annotation.name) {
|
|
683
|
+
throw new Error(`Portable reflection ${context} annotation ${index} is malformed`);
|
|
684
|
+
}
|
|
685
|
+
assertUint8(annotation.type, `${context} annotation ${index} type`);
|
|
686
|
+
if (annotation.type === 3) {
|
|
687
|
+
if (typeof annotation.stringValue !== "string" || Object.prototype.hasOwnProperty.call(annotation, "rawValue")) {
|
|
688
|
+
throw new Error(`Portable reflection ${context} string annotation is malformed`);
|
|
689
|
+
}
|
|
690
|
+
} else if (annotation.type <= 2) {
|
|
691
|
+
assertUint(annotation.rawValue, `${context} annotation ${index} rawValue`);
|
|
692
|
+
if (Object.prototype.hasOwnProperty.call(annotation, "stringValue")) {
|
|
693
|
+
throw new Error(`Portable reflection ${context} numeric annotation is malformed`);
|
|
694
|
+
}
|
|
695
|
+
} else {
|
|
696
|
+
throw new Error(`Portable reflection ${context} annotation type is unsupported`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function validateRegisterIndexed(entries, field, context, validate, identityFor = null) {
|
|
701
|
+
const identities = new Set();
|
|
702
|
+
for (const [index, entry] of entries.entries()) {
|
|
703
|
+
if (!isRecord(entry)) {
|
|
704
|
+
throw new Error(`Portable reflection ${context} entry ${index} is malformed`);
|
|
705
|
+
}
|
|
706
|
+
assertUint(entry[field], `${context} entry ${index} ${field}`);
|
|
707
|
+
const identity = identityFor ? identityFor(entry) : entry[field];
|
|
708
|
+
if (identities.has(identity)) {
|
|
709
|
+
throw new Error(`Portable reflection ${context} entry ${index} is duplicated`);
|
|
710
|
+
}
|
|
711
|
+
identities.add(identity);
|
|
712
|
+
validate(entry);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function requireKey(value, expected, context, keys) {
|
|
716
|
+
if (value !== expected || keys.has(value)) {
|
|
717
|
+
throw new Error(`Portable reflection ${context} key is malformed or duplicated`);
|
|
718
|
+
}
|
|
719
|
+
keys.add(value);
|
|
720
|
+
}
|
|
721
|
+
function requireRecord(value, context) {
|
|
722
|
+
if (!isRecord(value)) {
|
|
723
|
+
throw new Error(`${context} must be an object`);
|
|
724
|
+
}
|
|
725
|
+
return value;
|
|
726
|
+
}
|
|
727
|
+
function requireExactKeys(value, allowed, context) {
|
|
728
|
+
requireRecord(value, context);
|
|
729
|
+
const actual = Reflect.ownKeys(value);
|
|
730
|
+
if (actual.length !== allowed.length || actual.some(key => typeof key !== "string" || !allowed.includes(key))) {
|
|
731
|
+
throw new Error(`${context} has unsupported or missing fields`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function isRecord(value) {
|
|
735
|
+
// Plain objects only. A class instance, Map, Date, or anything carrying a
|
|
736
|
+
// custom prototype can present exactly the right own keys while bringing
|
|
737
|
+
// behaviour with it, so reject them at every object position.
|
|
738
|
+
return isPlainObject(value);
|
|
739
|
+
}
|
|
740
|
+
function requireBytes(value, context) {
|
|
741
|
+
if (!(value instanceof Uint8Array)) {
|
|
742
|
+
throw new Error(`${context} must be Uint8Array bytes`);
|
|
743
|
+
}
|
|
744
|
+
return value;
|
|
745
|
+
}
|
|
746
|
+
function copyBytes(value) {
|
|
747
|
+
if (value === undefined || value === null) {
|
|
748
|
+
return new Uint8Array(0);
|
|
749
|
+
}
|
|
750
|
+
if (value instanceof Uint8Array) {
|
|
751
|
+
return Uint8Array.from(value);
|
|
752
|
+
}
|
|
753
|
+
if (value instanceof ArrayBuffer) {
|
|
754
|
+
return new Uint8Array(value.slice(0));
|
|
755
|
+
}
|
|
756
|
+
if (ArrayBuffer.isView(value)) {
|
|
757
|
+
return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
758
|
+
}
|
|
759
|
+
throw new TypeError("Portable reflection bytes must be a byte view");
|
|
760
|
+
}
|
|
761
|
+
function decodeEffectBodyFresh(effectRes, sourceRecord) {
|
|
762
|
+
const shader = new HlslShader();
|
|
763
|
+
const buffer = effectRes.m_data.subarray(sourceRecord.offset, sourceRecord.offset + sourceRecord.byteLength);
|
|
764
|
+
const ok = shader.GetEffect().Read(buffer, sourceRecord.byteLength, effectRes.m_version, effectRes.m_stringTable, effectRes.m_stringTableSize, effectRes.sourcePath, {
|
|
765
|
+
effectStateManager: new HlslEffectStateManager()
|
|
766
|
+
});
|
|
767
|
+
if (ok) shader.ProcessEffect();
|
|
768
|
+
return shader;
|
|
769
|
+
}
|
|
770
|
+
function normalizeBodySourceRecord(record, permutationIndex, sourceByteLength) {
|
|
771
|
+
const end = record?.offset + record?.size;
|
|
772
|
+
if (!record || record.index !== permutationIndex || !Number.isSafeInteger(record.offset) || record.offset < 0 || record.offset > UINT32_MAX || !Number.isSafeInteger(record.size) || record.size < 1 || record.size > UINT32_MAX || !Number.isSafeInteger(end) || record.end !== end || !Number.isSafeInteger(sourceByteLength) || end > sourceByteLength) {
|
|
773
|
+
throw new Error(`Portable effect reflection body index ${permutationIndex} ` + "disagrees with its source record");
|
|
774
|
+
}
|
|
775
|
+
return Object.freeze({
|
|
776
|
+
offset: record.offset,
|
|
777
|
+
byteLength: record.size
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
function validateDisjointBodySourceRecords(sourceRecords) {
|
|
781
|
+
const unique = new Map();
|
|
782
|
+
for (const record of sourceRecords) {
|
|
783
|
+
const key = `${record.offset}:${record.byteLength}`;
|
|
784
|
+
if (!unique.has(key)) unique.set(key, record);
|
|
785
|
+
}
|
|
786
|
+
const ordered = Array.from(unique.values()).sort((left, right) => left.offset - right.offset || left.byteLength - right.byteLength);
|
|
787
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
788
|
+
const previous = ordered[index - 1];
|
|
789
|
+
const current = ordered[index];
|
|
790
|
+
if (current.offset < previous.offset + previous.byteLength) {
|
|
791
|
+
throw new Error("Portable effect reflection body source records partially overlap");
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
function fingerprintBytes(bytes) {
|
|
796
|
+
let hash = 0x811c9dc5;
|
|
797
|
+
for (const value of bytes) {
|
|
798
|
+
hash = Math.imul(hash ^ value, 0x01000193) >>> 0;
|
|
799
|
+
}
|
|
800
|
+
return `${bytes.byteLength}:${hash.toString(16).padStart(8, "0")}`;
|
|
801
|
+
}
|
|
802
|
+
function bytesEqual(left, right) {
|
|
803
|
+
return left.byteLength === right.byteLength && left.every((value, index) => value === right[index]);
|
|
804
|
+
}
|
|
805
|
+
function assertUint8(value, context) {
|
|
806
|
+
assertUint(value, context);
|
|
807
|
+
if (value > UINT8_MAX) {
|
|
808
|
+
throw new Error(`${context} must fit uint8`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function assertUint(value, context) {
|
|
812
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > UINT32_MAX) {
|
|
813
|
+
throw new Error(`${context} must fit uint32`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Validate one stage or library input record on its own.
|
|
819
|
+
*
|
|
820
|
+
* The whole-document walk validates inputs in place; this exposes the same
|
|
821
|
+
* rules for a caller hydrating a single input outside that walk.
|
|
822
|
+
*
|
|
823
|
+
* @param {object} input Candidate portable input record.
|
|
824
|
+
* @param {string} [context] Diagnostic context used in error messages.
|
|
825
|
+
* @returns {object} The validated input record.
|
|
826
|
+
*/
|
|
827
|
+
function validateEffectBodyInput(input, context = "standalone input") {
|
|
828
|
+
validateInput(input, context);
|
|
829
|
+
return input;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Report whether a value is a valid portable body-reflection document.
|
|
834
|
+
*
|
|
835
|
+
* @param {any} value Candidate document.
|
|
836
|
+
* @returns {boolean} True when the document validates.
|
|
837
|
+
*/
|
|
838
|
+
function isEffectBodyReflection(value) {
|
|
839
|
+
try {
|
|
840
|
+
validateEffectBodyReflection(value);
|
|
841
|
+
return true;
|
|
842
|
+
} catch {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
export { EFFECT_BODY_REFLECTION_FORMAT, EFFECT_BODY_REFLECTION_VERSION, buildEffectBodyReflection, enumerateUniqueEffectBodies, isEffectBodyReflection, readEffectBodyReflection, validateEffectBodyInput, validateEffectBodyReflection };
|
|
848
|
+
//# sourceMappingURL=portableReflection.js.map
|