@carbonenginejs/runtime-resource 0.15.0 → 0.17.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/CjsFormat.js +84 -83
- package/dist/format/CjsFormat.js.map +1 -1
- package/dist/format/CjsResourceProbe.js +87 -86
- package/dist/format/CjsResourceProbe.js.map +1 -1
- package/dist/format/carbonEffect/backendEngineId.js +100 -0
- package/dist/format/carbonEffect/backendEngineId.js.map +1 -0
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js +23 -23
- package/dist/format/carbonEffect/carbonEffectBackendBlock.js.map +1 -1
- package/dist/format/index.js +1 -1
- package/dist/formats/png/core/helpers.js +27 -8
- package/dist/formats/png/core/helpers.js.map +1 -1
- package/dist/formats/webgl/core/effectPackage.js +141 -53
- package/dist/formats/webgl/core/effectPackage.js.map +1 -1
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js +799 -456
- package/dist/formats/webgl/core/glsl/DxbcGlslEmitter.js.map +1 -1
- package/dist/formats/webgl/core/glslBackendBlock.js +70 -27
- package/dist/formats/webgl/core/glslBackendBlock.js.map +1 -1
- package/dist/formats/webgl/core/helpers.js +53 -53
- package/dist/formats/webgl/core/helpers.js.map +1 -1
- package/dist/formats/webgl/core/readGlslEffectContainer.js +82 -67
- package/dist/formats/webgl/core/readGlslEffectContainer.js.map +1 -1
- package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js +10 -1
- package/dist/formats/webgpu/core/carbonWebgpu/CarbonWebgpuContainer.js.map +1 -1
- package/dist/formats/wem/CjsWemFormat.js +121 -121
- package/dist/formats/wem/CjsWemFormat.js.map +1 -1
- package/dist/formats/wem/core/resolve.js +8 -8
- package/dist/formats/wem/core/resolve.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js +2 -2
- package/dist/resource/shader/reflection/Tr2EffectParameterAnnotation.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectResource.js +2 -2
- package/dist/resource/shader/reflection/Tr2EffectResource.js.map +1 -1
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js +102 -60
- package/dist/resource/shader/reflection/Tr2EffectStageInput.js.map +1 -1
- package/dist/resource/texture/Tr2TexturePipelineStepCompress.js +2 -2
- package/dist/resource/texture/Tr2TexturePipelineStepCompress.js.map +1 -1
- package/dist/resource/texture/Tr2TexturePipelineStepPack.js +2 -2
- package/dist/resource/texture/Tr2TexturePipelineStepPack.js.map +1 -1
- package/docs/concepts/format-type-resolution.md +79 -0
- package/docs/formats/README.md +11 -0
- package/docs/formats/wwise.md +25 -0
- package/docs/roadmap.md +19 -0
- package/package.json +61 -61
|
@@ -5,20 +5,25 @@ import { DxbcGlslHelperRegistry } from './DxbcGlslHelpers.js';
|
|
|
5
5
|
|
|
6
6
|
const COMPONENTS = ["x", "y", "z", "w"];
|
|
7
7
|
|
|
8
|
-
/**
|
|
9
|
-
* Map-style compute lowering caps simultaneous UAV writes to this many
|
|
10
|
-
* fragment color outputs. WebGL2 only guarantees `MAX_DRAW_BUFFERS >= 4`
|
|
11
|
-
* (real implementations commonly expose up to 8); a compute kernel that
|
|
12
|
-
* writes more UAVs than fit in one draw call's attachment set (e.g. a
|
|
13
|
-
* hierarchical mip-chain "Pack" pass writing 16 mip levels per dispatch)
|
|
14
|
-
* cannot become a single map-style fragment pass at all.
|
|
8
|
+
/**
|
|
9
|
+
* Map-style compute lowering caps simultaneous UAV writes to this many
|
|
10
|
+
* fragment color outputs. WebGL2 only guarantees `MAX_DRAW_BUFFERS >= 4`
|
|
11
|
+
* (real implementations commonly expose up to 8); a compute kernel that
|
|
12
|
+
* writes more UAVs than fit in one draw call's attachment set (e.g. a
|
|
13
|
+
* hierarchical mip-chain "Pack" pass writing 16 mip levels per dispatch)
|
|
14
|
+
* cannot become a single map-style fragment pass at all.
|
|
15
15
|
*/
|
|
16
16
|
const MAX_MAP_STYLE_UAV_OUTPUTS = 8;
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* GLSL sampler type per DXBC resource-dimension id (float return types; the
|
|
20
|
-
* stripped-RDEF corpus reflects everything as float).
|
|
18
|
+
/**
|
|
19
|
+
* GLSL sampler type per DXBC resource-dimension id (float return types; the
|
|
20
|
+
* stripped-RDEF corpus reflects everything as float).
|
|
21
21
|
*/
|
|
22
|
+
/**
|
|
23
|
+
* DXBC resource dimension for a cube map. Emulated addressing refuses it: a
|
|
24
|
+
* cube coordinate is a direction, so a [0,1] range test is meaningless there.
|
|
25
|
+
*/
|
|
26
|
+
const CUBE_DIMENSION = 6;
|
|
22
27
|
const SAMPLER_TYPE_BY_DIMENSION = {
|
|
23
28
|
2: "sampler2D",
|
|
24
29
|
3: "sampler2D",
|
|
@@ -27,11 +32,11 @@ const SAMPLER_TYPE_BY_DIMENSION = {
|
|
|
27
32
|
8: "sampler2DArray"
|
|
28
33
|
};
|
|
29
34
|
|
|
30
|
-
/**
|
|
31
|
-
* Shadow-sampler type per WebGL2-supported DXBC resource dimension.
|
|
32
|
-
* Comparison sampling changes both the call signature and the uniform type;
|
|
33
|
-
* keeping this separate from the ordinary sampler table guarantees shaders
|
|
34
|
-
* without comparison opcodes retain their existing declarations verbatim.
|
|
35
|
+
/**
|
|
36
|
+
* Shadow-sampler type per WebGL2-supported DXBC resource dimension.
|
|
37
|
+
* Comparison sampling changes both the call signature and the uniform type;
|
|
38
|
+
* keeping this separate from the ordinary sampler table guarantees shaders
|
|
39
|
+
* without comparison opcodes retain their existing declarations verbatim.
|
|
35
40
|
*/
|
|
36
41
|
const SHADOW_SAMPLER_TYPE_BY_DIMENSION = {
|
|
37
42
|
3: "sampler2DShadow",
|
|
@@ -41,9 +46,9 @@ const SHADOW_SAMPLER_TYPE_BY_DIMENSION = {
|
|
|
41
46
|
const COMPARISON_SAMPLE_OPCODES = new Set(["sample_c", "sample_c_lz"]);
|
|
42
47
|
const NON_COMPARISON_TEXTURE_OPCODES = new Set(["sample", "sample_l", "sample_b", "sample_d", "gather4", "gather4_po", "ld", "ld_ms", "lod"]);
|
|
43
48
|
|
|
44
|
-
/**
|
|
45
|
-
* Filtered-sample coordinate mask per resource dimension
|
|
46
|
-
* (HLSLcc `TranslateTexCoord`, toGLSLInstruction.cpp:985-1031).
|
|
49
|
+
/**
|
|
50
|
+
* Filtered-sample coordinate mask per resource dimension
|
|
51
|
+
* (HLSLcc `TranslateTexCoord`, toGLSLInstruction.cpp:985-1031).
|
|
47
52
|
*/
|
|
48
53
|
/** GLSL symbol for the merged detail-map array. */
|
|
49
54
|
const DETAIL_MAP_ARRAY_SYMBOL = "sDetailArrayMap";
|
|
@@ -57,32 +62,32 @@ const COORD_MASK_BY_DIMENSION = {
|
|
|
57
62
|
10: "xyzw"
|
|
58
63
|
};
|
|
59
64
|
|
|
60
|
-
/**
|
|
61
|
-
* DXBC -> GLSL ES 3.00 emitter for vertex and pixel stages.
|
|
62
|
-
*
|
|
63
|
-
* Implements the lowering rules in `docs/dxbc-lowering/*.md`, which pin every
|
|
64
|
-
* template to HLSLcc's `toGLSLInstruction.cpp`/`toGLSLDeclaration.cpp`.
|
|
65
|
-
* Registers are stored as float vec4s with bitcasts at use sites, comparison
|
|
66
|
-
* results are 0xFFFFFFFF/0 masks (never bools), and register-stable symbols
|
|
67
|
-
* (`cb#.data[]`, `t#`, `in_SEMANTIC#`, `vs_SEMANTIC#`) form the ABI surface.
|
|
68
|
-
*
|
|
69
|
-
* Emitter-wide policy: Adreno-3xx driver hygiene from HLSLcc (per-component
|
|
70
|
-
* bitwise splits, `op_not`, `uint(0)` literals, `uvecN()` ctor wraps) is NOT
|
|
71
|
-
* ported; the target is ANGLE-backed WebGL2 and the plain spec-legal forms
|
|
72
|
-
* are emitted instead.
|
|
65
|
+
/**
|
|
66
|
+
* DXBC -> GLSL ES 3.00 emitter for vertex and pixel stages.
|
|
67
|
+
*
|
|
68
|
+
* Implements the lowering rules in `docs/dxbc-lowering/*.md`, which pin every
|
|
69
|
+
* template to HLSLcc's `toGLSLInstruction.cpp`/`toGLSLDeclaration.cpp`.
|
|
70
|
+
* Registers are stored as float vec4s with bitcasts at use sites, comparison
|
|
71
|
+
* results are 0xFFFFFFFF/0 masks (never bools), and register-stable symbols
|
|
72
|
+
* (`cb#.data[]`, `t#`, `in_SEMANTIC#`, `vs_SEMANTIC#`) form the ABI surface.
|
|
73
|
+
*
|
|
74
|
+
* Emitter-wide policy: Adreno-3xx driver hygiene from HLSLcc (per-component
|
|
75
|
+
* bitwise splits, `op_not`, `uint(0)` literals, `uvecN()` ctor wraps) is NOT
|
|
76
|
+
* ported; the target is ANGLE-backed WebGL2 and the plain spec-legal forms
|
|
77
|
+
* are emitted instead.
|
|
73
78
|
*/
|
|
74
79
|
class DxbcGlslEmitter {
|
|
75
|
-
/**
|
|
76
|
-
* @param {object} [options] Emitter options.
|
|
77
|
-
* @param {object} [options.profile] Target-runtime profile overrides.
|
|
78
|
-
* @param {"array"|"std140"} [options.profile.constantBufferStyle] `uniform vec4 cbN[]`
|
|
79
|
-
* arrays for ccpwgl's uniform4fv path, or std140 blocks.
|
|
80
|
-
* @param {Object.<number,number>} [options.profile.pixelConstantBufferRemap] Pixel-stage
|
|
81
|
-
* cb slot renames (ccpwgl keeps PS effect constants at cb7).
|
|
82
|
-
* @param {function(number,string):string} [options.profile.samplerName] Texture uniform
|
|
83
|
-
* naming per register and stage.
|
|
84
|
-
* @param {number} [options.profile.vertexStructuredCapacity] Element capacity for
|
|
85
|
-
* vertex-stage structured-buffer UBOs (bones; Carbon max is 69 joints).
|
|
80
|
+
/**
|
|
81
|
+
* @param {object} [options] Emitter options.
|
|
82
|
+
* @param {object} [options.profile] Target-runtime profile overrides.
|
|
83
|
+
* @param {"array"|"std140"} [options.profile.constantBufferStyle] `uniform vec4 cbN[]`
|
|
84
|
+
* arrays for ccpwgl's uniform4fv path, or std140 blocks.
|
|
85
|
+
* @param {Object.<number,number>} [options.profile.pixelConstantBufferRemap] Pixel-stage
|
|
86
|
+
* cb slot renames (ccpwgl keeps PS effect constants at cb7).
|
|
87
|
+
* @param {function(number,string):string} [options.profile.samplerName] Texture uniform
|
|
88
|
+
* naming per register and stage.
|
|
89
|
+
* @param {number} [options.profile.vertexStructuredCapacity] Element capacity for
|
|
90
|
+
* vertex-stage structured-buffer UBOs (bones; Carbon max is 69 joints).
|
|
86
91
|
*/
|
|
87
92
|
constructor(options = {}) {
|
|
88
93
|
this.helpers = new DxbcGlslHelperRegistry();
|
|
@@ -124,13 +129,54 @@ class DxbcGlslEmitter {
|
|
|
124
129
|
};
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
/**
|
|
128
|
-
* Registers the GLSL helper functions this emitter's lowerings can require.
|
|
129
|
-
* GLSL ES 3.00 has no `bitfieldExtract`/`bitfieldInsert`/`bitCount`/
|
|
130
|
-
* `textureGather` (all ES 3.10+), so `ibfe`/`ubfe`/`bfi`/`countbits`/
|
|
131
|
-
* `gather4` lower to these hand-written equivalents instead.
|
|
132
|
+
/**
|
|
133
|
+
* Registers the GLSL helper functions this emitter's lowerings can require.
|
|
134
|
+
* GLSL ES 3.00 has no `bitfieldExtract`/`bitfieldInsert`/`bitCount`/
|
|
135
|
+
* `textureGather` (all ES 3.10+), so `ibfe`/`ubfe`/`bfi`/`countbits`/
|
|
136
|
+
* `gather4` lower to these hand-written equivalents instead.
|
|
132
137
|
*/
|
|
133
138
|
_defineHelpers() {
|
|
139
|
+
// Texture address modes WebGL2 cannot express, emulated in the shader.
|
|
140
|
+
//
|
|
141
|
+
// WebGL2 does REPEAT, MIRRORED_REPEAT and CLAMP_TO_EDGE natively. It has
|
|
142
|
+
// no CLAMP_TO_BORDER (that needs EXT_texture_border_clamp, absent on our
|
|
143
|
+
// desktop contexts) and no MIRROR_ONCE. Only those two are done here.
|
|
144
|
+
//
|
|
145
|
+
// The mode is a RUNTIME value, per axis, read from the address buffer.
|
|
146
|
+
// It cannot be baked: ccpwgl resolves sampler overrides after
|
|
147
|
+
// translation and caches one translated GLSL per resource path across
|
|
148
|
+
// every instance, so a compile-time mode is wrong for any object whose
|
|
149
|
+
// override differs from the container - in both directions.
|
|
150
|
+
//
|
|
151
|
+
// Modes are the Trinity enum as stored: 1 wrap, 2 mirror, 3 clamp-edge,
|
|
152
|
+
// 4 border, 5 mirror-once. **0 means "nothing to emulate"**, which is
|
|
153
|
+
// what every failure produces - a zeroed buffer, an absent upload, a
|
|
154
|
+
// texture the consumer did not know about. Carbon's enum starts at 1 and
|
|
155
|
+
// no shipped sampler carries 0, so 0 cannot swallow a real mode.
|
|
156
|
+
//
|
|
157
|
+
// Two arities rather than one vec3 form: a 2D sample has no third
|
|
158
|
+
// coordinate, and its sampler may still declare a W mode - eight shipped
|
|
159
|
+
// samplers declare border on W - which must not be tested against a
|
|
160
|
+
// component that does not exist.
|
|
161
|
+
this.helpers.define("cjsAddressCoord", {
|
|
162
|
+
source: ["vec2 cjsAddressCoord(vec2 uv, vec2 modes) {", " if (int(modes.x) == 5) uv.x = clamp(abs(uv.x), 0.0, 1.0);", " if (int(modes.y) == 5) uv.y = clamp(abs(uv.y), 0.0, 1.0);", " return uv;", "}", "", "vec3 cjsAddressCoord(vec3 uv, vec3 modes) {", " if (int(modes.x) == 5) uv.x = clamp(abs(uv.x), 0.0, 1.0);", " if (int(modes.y) == 5) uv.y = clamp(abs(uv.y), 0.0, 1.0);", " if (int(modes.z) == 5) uv.z = clamp(abs(uv.z), 0.0, 1.0);", " return uv;", "}"].join("\n")
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Applied to the already sampled value, so it composes with every sample
|
|
166
|
+
// form the emitter produces - texture, textureLod, textureGrad and the
|
|
167
|
+
// bias variant - instead of needing an overload for each.
|
|
168
|
+
//
|
|
169
|
+
// The border colour is baked, not read from the buffer: Carbon's
|
|
170
|
+
// AddSamplerOverride takes only U and V, so no override can change it.
|
|
171
|
+
// It is NOT always transparent black - specialfx/cloud, cloudsimple and
|
|
172
|
+
// volumetrichalfsphereglow use opaque white.
|
|
173
|
+
//
|
|
174
|
+
// Testing the post-transform coordinate is safe: an axis is either
|
|
175
|
+
// mirror-once or border, never both, and mirror-once leaves other axes
|
|
176
|
+
// untouched.
|
|
177
|
+
this.helpers.define("cjsAddressBorder", {
|
|
178
|
+
source: ["vec4 cjsAddressBorder(vec4 sampled, vec2 uv, vec2 modes, vec4 borderColor) {", " if (int(modes.x) == 4 && (uv.x < 0.0 || uv.x > 1.0)) return borderColor;", " if (int(modes.y) == 4 && (uv.y < 0.0 || uv.y > 1.0)) return borderColor;", " return sampled;", "}", "", "vec4 cjsAddressBorder(vec4 sampled, vec3 uv, vec3 modes, vec4 borderColor) {", " if (int(modes.x) == 4 && (uv.x < 0.0 || uv.x > 1.0)) return borderColor;", " if (int(modes.y) == 4 && (uv.y < 0.0 || uv.y > 1.0)) return borderColor;", " if (int(modes.z) == 4 && (uv.z < 0.0 || uv.z > 1.0)) return borderColor;", " return sampled;", "}"].join("\n")
|
|
179
|
+
});
|
|
134
180
|
// D3D11 IBFE pseudocode: width = src0 & 0x1f, offset = src1 & 0x1f;
|
|
135
181
|
// width == 0 -> 0; width + offset < 32 -> a left/right arithmetic-shift
|
|
136
182
|
// pair that sign-extends from bit (width-1); else an arithmetic shift by
|
|
@@ -173,16 +219,16 @@ class DxbcGlslEmitter {
|
|
|
173
219
|
});
|
|
174
220
|
}
|
|
175
221
|
|
|
176
|
-
/**
|
|
177
|
-
* Translates one DXBC stage into GLSL ES 3.00 source.
|
|
178
|
-
*
|
|
179
|
-
* @param {ArrayBuffer|ArrayBufferView|Uint8Array} bytes DXBC container bytes.
|
|
180
|
-
* @param {object} [options] Emit options.
|
|
181
|
-
* @param {string} [options.source] Source name used in error details.
|
|
182
|
-
* @returns {{source:string,stageName:string,inputs:object[],outputs:object[],bindings:object[],warnings:string[],computeFragment:(object|undefined)}}
|
|
183
|
-
* GLSL text plus the IO contract the packaging layer records; compute
|
|
184
|
-
* stages add a `computeFragment` host contract (thread group,
|
|
185
|
-
* dispatch-origin uniform, per-output UAV slice routing).
|
|
222
|
+
/**
|
|
223
|
+
* Translates one DXBC stage into GLSL ES 3.00 source.
|
|
224
|
+
*
|
|
225
|
+
* @param {ArrayBuffer|ArrayBufferView|Uint8Array} bytes DXBC container bytes.
|
|
226
|
+
* @param {object} [options] Emit options.
|
|
227
|
+
* @param {string} [options.source] Source name used in error details.
|
|
228
|
+
* @returns {{source:string,stageName:string,inputs:object[],outputs:object[],bindings:object[],warnings:string[],computeFragment:(object|undefined)}}
|
|
229
|
+
* GLSL text plus the IO contract the packaging layer records; compute
|
|
230
|
+
* stages add a `computeFragment` host contract (thread group,
|
|
231
|
+
* dispatch-origin uniform, per-output UAV slice routing).
|
|
186
232
|
*/
|
|
187
233
|
Emit(bytes, options = {}) {
|
|
188
234
|
const sourceName = options.source || "memory";
|
|
@@ -214,12 +260,20 @@ class DxbcGlslEmitter {
|
|
|
214
260
|
outputNames: new Map(),
|
|
215
261
|
resourceDimensions: new Map(),
|
|
216
262
|
resourceNames: new Map(),
|
|
217
|
-
|
|
263
|
+
...(() => {
|
|
264
|
+
const analysed = this._analyzeComparisonResources(raw.decoder, sourceName);
|
|
265
|
+
return {
|
|
266
|
+
comparisonResources: analysed.comparison,
|
|
267
|
+
pairedSamplers: analysed.paired
|
|
268
|
+
};
|
|
269
|
+
})(),
|
|
218
270
|
constantBufferNames: new Map(),
|
|
219
271
|
structuredBuffers: new Map(),
|
|
220
272
|
lightConstantBuffer: this._normalizeLightConstantBufferProfile(),
|
|
221
273
|
lightConstantBufferDeclared: false,
|
|
222
274
|
lightPackedTexture: this._normalizeLightPackedTextureProfile(),
|
|
275
|
+
emulatedAddressing: this._normalizeEmulatedAddressingProfile(),
|
|
276
|
+
addressBufferDeclared: false,
|
|
223
277
|
lightPackedTextureDeclared: false,
|
|
224
278
|
// Resource (t#) registers stubbed to a compile-time zero: their
|
|
225
279
|
// declarations/bindings are dropped and every read of them lowers
|
|
@@ -361,10 +415,10 @@ class DxbcGlslEmitter {
|
|
|
361
415
|
return result;
|
|
362
416
|
}
|
|
363
417
|
|
|
364
|
-
/**
|
|
365
|
-
* Normalizes the optional local-light constant-buffer lowering profile.
|
|
366
|
-
*
|
|
367
|
-
* @returns {object|null} Normalized profile, or null when disabled.
|
|
418
|
+
/**
|
|
419
|
+
* Normalizes the optional local-light constant-buffer lowering profile.
|
|
420
|
+
*
|
|
421
|
+
* @returns {object|null} Normalized profile, or null when disabled.
|
|
368
422
|
*/
|
|
369
423
|
_normalizeLightConstantBufferProfile() {
|
|
370
424
|
const profile = this.profile.lightConstantBuffer;
|
|
@@ -383,30 +437,52 @@ class DxbcGlslEmitter {
|
|
|
383
437
|
};
|
|
384
438
|
}
|
|
385
439
|
|
|
386
|
-
/**
|
|
387
|
-
* Finds resources used by comparison-sample instructions before resource
|
|
388
|
-
* declarations are emitted, so those declarations can use shadow sampler
|
|
389
|
-
* types. A single GLSL uniform cannot be both a shadow and ordinary sampler;
|
|
390
|
-
* reject that shape explicitly instead of producing invalid GLSL.
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
440
|
+
/**
|
|
441
|
+
* Finds resources used by comparison-sample instructions before resource
|
|
442
|
+
* declarations are emitted, so those declarations can use shadow sampler
|
|
443
|
+
* types. A single GLSL uniform cannot be both a shadow and ordinary sampler;
|
|
444
|
+
* reject that shape explicitly instead of producing invalid GLSL.
|
|
445
|
+
*
|
|
446
|
+
* The same walk records the t#/s# pairing for EVERY sampled resource, not
|
|
447
|
+
* only the comparison ones. D3D carries sampler state on a shared sampler
|
|
448
|
+
* object and pairs it with a texture at the sample site; GLSL merges the two
|
|
449
|
+
* into one uniform, so that pairing is the only place the pairing exists once
|
|
450
|
+
* the shader is translated. Dropping it forces a consumer to guess, and the
|
|
451
|
+
* available guess - "the sampler whose register equals the texture's" - is
|
|
452
|
+
* numeric coincidence: with two pattern samplers declared, `NormalMap` at t3
|
|
453
|
+
* collides with the clamp sampler at s3 and every hull's normal map is read
|
|
454
|
+
* clamped instead of wrapped.
|
|
455
|
+
*
|
|
456
|
+
* @param {object} decoder Decoded DXBC instruction stream.
|
|
457
|
+
* @param {string} sourceName Source name used in error details.
|
|
458
|
+
* @returns {{comparison:Map<number,Set<number>>,paired:Map<number,Set<number>>}}
|
|
459
|
+
* Resource register -> sampler registers, for comparison sampling and for
|
|
460
|
+
* all sampling respectively.
|
|
395
461
|
*/
|
|
396
462
|
_analyzeComparisonResources(decoder, sourceName) {
|
|
397
463
|
const comparisonResources = new Map();
|
|
464
|
+
const pairedSamplers = new Map();
|
|
398
465
|
const ordinaryResources = new Set();
|
|
466
|
+
const recordPair = (map, register, samplerRegister) => {
|
|
467
|
+
const samplers = map.get(register) || new Set();
|
|
468
|
+
if (Number.isInteger(samplerRegister)) samplers.add(samplerRegister);
|
|
469
|
+
map.set(register, samplers);
|
|
470
|
+
};
|
|
399
471
|
for (const instruction of decoder.instructions) {
|
|
400
472
|
const resourceOperand = instruction.operands?.[2];
|
|
401
473
|
if (!resourceOperand || !Number.isInteger(resourceOperand.registerIndex)) continue;
|
|
474
|
+
const register = resourceOperand.registerIndex;
|
|
475
|
+
const samplerRegister = instruction.operands?.[3]?.registerIndex;
|
|
402
476
|
if (COMPARISON_SAMPLE_OPCODES.has(instruction.opcodeName)) {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const samplers = comparisonResources.get(register) || new Set();
|
|
406
|
-
if (Number.isInteger(samplerRegister)) samplers.add(samplerRegister);
|
|
407
|
-
comparisonResources.set(register, samplers);
|
|
477
|
+
recordPair(comparisonResources, register, samplerRegister);
|
|
478
|
+
recordPair(pairedSamplers, register, samplerRegister);
|
|
408
479
|
} else if (NON_COMPARISON_TEXTURE_OPCODES.has(instruction.opcodeName)) {
|
|
409
|
-
ordinaryResources.add(
|
|
480
|
+
ordinaryResources.add(register);
|
|
481
|
+
// `ld`-family fetches carry no sampler operand and legitimately
|
|
482
|
+
// pair with nothing; only record a pair when one is present.
|
|
483
|
+
if (Number.isInteger(samplerRegister)) {
|
|
484
|
+
recordPair(pairedSamplers, register, samplerRegister);
|
|
485
|
+
}
|
|
410
486
|
}
|
|
411
487
|
}
|
|
412
488
|
for (const register of comparisonResources.keys()) {
|
|
@@ -417,13 +493,258 @@ class DxbcGlslEmitter {
|
|
|
417
493
|
});
|
|
418
494
|
}
|
|
419
495
|
}
|
|
420
|
-
return
|
|
496
|
+
return {
|
|
497
|
+
comparison: comparisonResources,
|
|
498
|
+
paired: pairedSamplers
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Normalizes the emulated-addressing profile.
|
|
504
|
+
*
|
|
505
|
+
* Keyed by RESOURCE register, not sampler register, because that is what
|
|
506
|
+
* every layer we touch keys on: GL stores wrap state on the texture object,
|
|
507
|
+
* ccpwgl keys overrides by texture name, and the emitted GLSL declares one
|
|
508
|
+
* sampler uniform per resource. Only D3D treats the mode as a property of a
|
|
509
|
+
* shared sampler - in `decalv5` five textures share one bordered sampler and
|
|
510
|
+
* each can resolve to a different mode, which a sampler-keyed scheme cannot
|
|
511
|
+
* represent.
|
|
512
|
+
*
|
|
513
|
+
* It arrives as a profile option because it CANNOT be discovered here: DXBC
|
|
514
|
+
* carries no sampler state, which lives in the Carbon container wrapping it.
|
|
515
|
+
* The caller also chooses the SUPERSET - listing a texture whose container
|
|
516
|
+
* mode needs no emulation is deliberate and cheap, because the runtime mode
|
|
517
|
+
* decides, and it is what lets an override correct a container that is wrong.
|
|
518
|
+
*
|
|
519
|
+
* Shape: `{ bufferRegister, textures: [{ registerIndex, borderColor }] }`.
|
|
520
|
+
*
|
|
521
|
+
* @returns {{bufferRegister:number, textures:Map<number,{color:number[]}>}|null}
|
|
522
|
+
* Null when disabled.
|
|
523
|
+
*/
|
|
524
|
+
_normalizeEmulatedAddressingProfile() {
|
|
525
|
+
const value = this.profile.emulatedAddressing;
|
|
526
|
+
|
|
527
|
+
// Either input alone is a complete profile: `samplerModes` is the
|
|
528
|
+
// ordinary case (container modes, mapped onto resources here), and
|
|
529
|
+
// `textures` is for a caller gating something the container gives it no
|
|
530
|
+
// reason to gate. Requiring both would reject the ordinary case.
|
|
531
|
+
if (!value || typeof value !== "object") return null;
|
|
532
|
+
if (!Array.isArray(value.textures) && !value.samplerModes) return null;
|
|
533
|
+
const bufferRegister = Number(value.bufferRegister ?? 8);
|
|
534
|
+
if (!Number.isInteger(bufferRegister) || bufferRegister < 0 || bufferRegister > 254) {
|
|
535
|
+
throw new WebglReadError("Emulated-addressing buffer register must be an integer in 0..254", {
|
|
536
|
+
bufferRegister: value.bufferRegister
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
const colorOf = raw => {
|
|
540
|
+
if (raw === undefined || raw === null) return [0, 0, 0, 0];
|
|
541
|
+
if (!Array.isArray(raw) || raw.length !== 4) {
|
|
542
|
+
throw new WebglReadError("Border colour must be four components", {
|
|
543
|
+
color: raw
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
const parsed = raw.map(Number);
|
|
547
|
+
|
|
548
|
+
// A malformed colour must not become a plausible one: transparent
|
|
549
|
+
// black is the common case, so silently defaulting to it would hide
|
|
550
|
+
// the mistake in exactly the effects that differ from it.
|
|
551
|
+
if (parsed.some(c => !Number.isFinite(c))) {
|
|
552
|
+
throw new WebglReadError("Border colour components must be finite numbers", {
|
|
553
|
+
color: raw
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
return parsed;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// Container sampler modes, keyed by SAMPLER register. Supplied so the
|
|
560
|
+
// emitter can derive which resources need a gate: only it knows, from
|
|
561
|
+
// the DXBC, which resource is read through which sampler. The caller
|
|
562
|
+
// knows the modes but not that mapping; the emitter knows the mapping
|
|
563
|
+
// but not the modes, because DXBC carries no sampler state.
|
|
564
|
+
const samplerModes = new Map();
|
|
565
|
+
for (const [key, modes] of Object.entries(value.samplerModes ?? {})) {
|
|
566
|
+
const registerIndex = Number(key);
|
|
567
|
+
if (!Number.isInteger(registerIndex) || registerIndex < 0) continue;
|
|
568
|
+
const axes = [modes?.u, modes?.v, modes?.w].map(Number);
|
|
569
|
+
|
|
570
|
+
// Only the two modes WebGL2 cannot express need a gate. Everything
|
|
571
|
+
// else is left to GL, so listing it would cost a branch for nothing.
|
|
572
|
+
if (axes.some(m => m === 4 || m === 5)) {
|
|
573
|
+
samplerModes.set(registerIndex, true);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const textures = new Map();
|
|
577
|
+
for (const entry of value.textures ?? []) {
|
|
578
|
+
const registerIndex = Number.isInteger(entry) ? entry : Number(entry?.registerIndex);
|
|
579
|
+
if (!Number.isInteger(registerIndex) || registerIndex < 0) {
|
|
580
|
+
throw new WebglReadError("Emulated-addressing entries need an integer resource registerIndex", {
|
|
581
|
+
entry
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
textures.set(registerIndex, {
|
|
585
|
+
color: colorOf(Number.isInteger(entry) ? null : entry?.borderColor)
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (!textures.size && !samplerModes.size) return null;
|
|
589
|
+
return {
|
|
590
|
+
bufferRegister,
|
|
591
|
+
textures,
|
|
592
|
+
samplerModes,
|
|
593
|
+
defaultColor: colorOf(value.borderColor)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Applies emulated address modes to one sample.
|
|
599
|
+
*
|
|
600
|
+
* Emits nothing for a texture the caller did not list, so a shader with no
|
|
601
|
+
* emulated addressing is byte-identical to one emitted without the profile.
|
|
602
|
+
*
|
|
603
|
+
* @param {object} state Emit state.
|
|
604
|
+
* @param {object} instruction Decoded texture instruction.
|
|
605
|
+
* @param {string} call The GLSL sample expression built so far.
|
|
606
|
+
* @param {string} coord The coordinate expression passed to that sample.
|
|
607
|
+
* @param {string} coordMask Component mask of the coordinate.
|
|
608
|
+
* @returns {string} The sample expression, addressed when the caller listed it.
|
|
609
|
+
* @private
|
|
610
|
+
*/
|
|
611
|
+
_applyEmulatedAddressing(state, instruction, call, coord, coordMask) {
|
|
612
|
+
const profile = state.emulatedAddressing;
|
|
613
|
+
if (!profile) return call;
|
|
614
|
+
const resourceRegister = instruction.operands?.[2]?.registerIndex;
|
|
615
|
+
if (!Number.isInteger(resourceRegister)) return call;
|
|
616
|
+
|
|
617
|
+
// An explicit entry wins; otherwise derive from the sampler this
|
|
618
|
+
// resource is read through. Deriving is the ordinary case - the caller
|
|
619
|
+
// supplies container modes and the emitter maps them onto resources -
|
|
620
|
+
// and the explicit list is for a caller that wants a texture gated the
|
|
621
|
+
// container gives it no reason to gate. That is not redundant: a
|
|
622
|
+
// sampler override can introduce an emulated mode the container never
|
|
623
|
+
// declared, and overrides exist precisely to correct a wrong container.
|
|
624
|
+
let entry = profile.textures.get(resourceRegister);
|
|
625
|
+
if (!entry) {
|
|
626
|
+
const samplerRegister = instruction.operands?.[3]?.registerIndex;
|
|
627
|
+
if (!Number.isInteger(samplerRegister)) return call;
|
|
628
|
+
if (!profile.samplerModes.has(samplerRegister)) return call;
|
|
629
|
+
entry = {
|
|
630
|
+
color: profile.defaultColor
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// A cube coordinate is a DIRECTION, not a texture coordinate, so a
|
|
635
|
+
// [0,1] test has no meaning on it. Refuse rather than emit a test that
|
|
636
|
+
// is silently nonsense - the caller must not list cube-target
|
|
637
|
+
// resources. decalholev5's interior cube and cubetextureviewer are the
|
|
638
|
+
// shipped cases that reach here.
|
|
639
|
+
const dimension = state.resourceDimensions.get(resourceRegister);
|
|
640
|
+
if (dimension === CUBE_DIMENSION) {
|
|
641
|
+
// Reported and skipped, not fatal. `decalholev5` reads its interior
|
|
642
|
+
// cube through a bordered sampler, so refusing would make a shipped
|
|
643
|
+
// effect unbuildable over a mode that cannot apply to it anyway -
|
|
644
|
+
// trading a missing border for no shader at all. Skipping silently
|
|
645
|
+
// is the other wrong answer, so it goes in warnings.
|
|
646
|
+
state.warnings.push(`emulated addressing skipped for cube resource t${resourceRegister}: ` + "a cube coordinate is a direction, so a [0,1] range test has no meaning");
|
|
647
|
+
return call;
|
|
648
|
+
}
|
|
649
|
+
if (coordMask.length !== 2 && coordMask.length !== 3) {
|
|
650
|
+
throw new WebglReadError("Emulated addressing supports two- and three-component sampling only", {
|
|
651
|
+
source: state.sourceName,
|
|
652
|
+
registerIndex: resourceRegister,
|
|
653
|
+
coordMask
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
const buffer = this._ensureAddressBuffer(state);
|
|
657
|
+
const swizzle = coordMask.length === 2 ? "xy" : "xyz";
|
|
658
|
+
const modes = `${buffer}[${resourceRegister}].${swizzle}`;
|
|
659
|
+
const glslFloat = n => Number.isInteger(n) ? `${n}.0` : String(n);
|
|
660
|
+
const color = entry.color.map(glslFloat).join(", ");
|
|
661
|
+
|
|
662
|
+
// Mirror-once rewrites the coordinate BEFORE the fetch; border tests
|
|
663
|
+
// after it. Both are emitted: an axis is only ever one of them, and the
|
|
664
|
+
// unused one reads mode 0 or a native mode and does nothing.
|
|
665
|
+
const coordHelper = this.helpers.require("cjsAddressCoord");
|
|
666
|
+
const borderHelper = this.helpers.require("cjsAddressBorder");
|
|
667
|
+
const addressedCoord = `${coordHelper}(${coord}, ${modes})`;
|
|
668
|
+
const rewritten = call.split(coord).join(addressedCoord);
|
|
669
|
+
return `${borderHelper}(${rewritten}, ${addressedCoord}, ${modes}, vec4(${color}))`;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Declares the emulated-addressing constant buffer, once per shader.
|
|
674
|
+
*
|
|
675
|
+
* Sized to the highest listed resource register so the array stays compact,
|
|
676
|
+
* the way Carbon emitters declare compact `cb` arrays. The register is
|
|
677
|
+
* caller-chosen and defaults to 8: shipped effects declare only cb0-4, 6 and
|
|
678
|
+
* 7, and ccpwgl already resolves `cb0`..`cb15` by name, so 8 needs no new
|
|
679
|
+
* binding table.
|
|
680
|
+
*
|
|
681
|
+
* @param {object} state Emit state.
|
|
682
|
+
* @returns {string} The buffer's GLSL name.
|
|
683
|
+
* @private
|
|
684
|
+
*/
|
|
685
|
+
_ensureAddressBuffer(state) {
|
|
686
|
+
const {
|
|
687
|
+
bufferRegister,
|
|
688
|
+
textures
|
|
689
|
+
} = state.emulatedAddressing;
|
|
690
|
+
const name = `cb${bufferRegister}`;
|
|
691
|
+
if (state.addressBufferDeclared) return name;
|
|
692
|
+
|
|
693
|
+
// Two declarations of one cb name is a GLSL redefinition error that
|
|
694
|
+
// nothing downstream checks, so catch it here, where the register is
|
|
695
|
+
// still attributable to a caller's choice.
|
|
696
|
+
//
|
|
697
|
+
// Compared by NAME, not by register: `constantBufferNames` is keyed by
|
|
698
|
+
// DXBC slot while the emitted name uses the remapped register, and on a
|
|
699
|
+
// pixel stage slot 0 becomes `cb7`. Comparing register to slot would
|
|
700
|
+
// miss exactly that case and emit two `cb7` declarations.
|
|
701
|
+
if ([...state.constantBufferNames.values()].includes(name)) {
|
|
702
|
+
throw new WebglReadError("Emulated-addressing buffer register collides with a declared constant buffer", {
|
|
703
|
+
source: state.sourceName,
|
|
704
|
+
bufferRegister
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// Sized from every resource the shader DECLARES, not from the addressed
|
|
709
|
+
// set: with a derived set the addressed registers are not all known yet,
|
|
710
|
+
// and the array is indexed by resource register, so it must be long
|
|
711
|
+
// enough for any of them. DXBC puts declarations before instructions, so
|
|
712
|
+
// resourceDimensions is complete by the first sample.
|
|
713
|
+
const highest = Math.max(-1, ...state.resourceDimensions.keys(), ...textures.keys());
|
|
714
|
+
const rows = highest + 1;
|
|
715
|
+
if (rows < 1) {
|
|
716
|
+
throw new WebglReadError("Emulated addressing found no declared resource to size its buffer", {
|
|
717
|
+
source: state.sourceName
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
state.constantBufferNames.set(bufferRegister, name);
|
|
721
|
+
state.addressBufferDeclared = true;
|
|
722
|
+
state.declarationLines.push(`uniform vec4 ${name}[${rows}];`);
|
|
723
|
+
// An ordinary constantBuffer binding, deliberately with no extra fields.
|
|
724
|
+
// `cjsSemantic` is reserved vocabulary for the local-light family and
|
|
725
|
+
// the block writer throws on any other value, and the wire drops fields
|
|
726
|
+
// it does not encode - so an invented one would vanish for every effect
|
|
727
|
+
// loaded from bytes, which is exactly how the packed-light branch came
|
|
728
|
+
// to be silently dead.
|
|
729
|
+
//
|
|
730
|
+
// The consumer identifies it by register instead: Carbon declares only
|
|
731
|
+
// cb0-4, 6 and 7 across all 537 shipped effects, so a constant buffer at
|
|
732
|
+
// 8 or above is ours. That is a convention, and it is written down in
|
|
733
|
+
// the addressing contract rather than left to be inferred.
|
|
734
|
+
state.bindings.push({
|
|
735
|
+
kind: "constantBuffer",
|
|
736
|
+
registerIndex: bufferRegister,
|
|
737
|
+
name,
|
|
738
|
+
sizeInVec4: rows,
|
|
739
|
+
style: "array"
|
|
740
|
+
});
|
|
741
|
+
return name;
|
|
421
742
|
}
|
|
422
743
|
|
|
423
|
-
/**
|
|
424
|
-
* Normalizes the optional packed local-light texture lowering profile.
|
|
425
|
-
*
|
|
426
|
-
* @returns {object|null} Normalized profile, or null when disabled.
|
|
744
|
+
/**
|
|
745
|
+
* Normalizes the optional packed local-light texture lowering profile.
|
|
746
|
+
*
|
|
747
|
+
* @returns {object|null} Normalized profile, or null when disabled.
|
|
427
748
|
*/
|
|
428
749
|
_normalizeLightPackedTextureProfile() {
|
|
429
750
|
const profile = this.profile.lightPackedTexture;
|
|
@@ -441,12 +762,12 @@ class DxbcGlslEmitter {
|
|
|
441
762
|
};
|
|
442
763
|
}
|
|
443
764
|
|
|
444
|
-
/**
|
|
445
|
-
* Declares the packed local-light texture ABI used to replace the
|
|
446
|
-
* LightIndexBuffer/LightBuffer/LightProfileArray trio with one RGBA32UI
|
|
447
|
-
* data texture.
|
|
448
|
-
*
|
|
449
|
-
* @param {object} state Emit state.
|
|
765
|
+
/**
|
|
766
|
+
* Declares the packed local-light texture ABI used to replace the
|
|
767
|
+
* LightIndexBuffer/LightBuffer/LightProfileArray trio with one RGBA32UI
|
|
768
|
+
* data texture.
|
|
769
|
+
*
|
|
770
|
+
* @param {object} state Emit state.
|
|
450
771
|
*/
|
|
451
772
|
_ensureLightPackedTexture(state) {
|
|
452
773
|
const profile = state.lightPackedTexture;
|
|
@@ -468,11 +789,11 @@ class DxbcGlslEmitter {
|
|
|
468
789
|
});
|
|
469
790
|
}
|
|
470
791
|
|
|
471
|
-
/**
|
|
472
|
-
* Declares the local-light constant-buffer ABI used to replace the two
|
|
473
|
-
* tiled-light structured buffers without consuming sampler units.
|
|
474
|
-
*
|
|
475
|
-
* @param {object} state Emit state.
|
|
792
|
+
/**
|
|
793
|
+
* Declares the local-light constant-buffer ABI used to replace the two
|
|
794
|
+
* tiled-light structured buffers without consuming sampler units.
|
|
795
|
+
*
|
|
796
|
+
* @param {object} state Emit state.
|
|
476
797
|
*/
|
|
477
798
|
_ensureLightConstantBuffer(state) {
|
|
478
799
|
const profile = state.lightConstantBuffer;
|
|
@@ -495,15 +816,15 @@ class DxbcGlslEmitter {
|
|
|
495
816
|
});
|
|
496
817
|
}
|
|
497
818
|
|
|
498
|
-
/**
|
|
499
|
-
* Rejects compute shaders that need real compute-pipeline features (shared
|
|
500
|
-
* memory, barriers, atomics, raw/structured/typed UAV reads) instead of the
|
|
501
|
-
* "map-style" thread-per-fragment shape this emitter lowers. The message
|
|
502
|
-
* intentionally contains "not supported" so packaging's kill-list
|
|
503
|
-
* classifier regex (`/not supported|No GLSL lowering|unimplementable/i`)
|
|
504
|
-
* routes these to the exclusion list rather than the failure list.
|
|
505
|
-
*
|
|
506
|
-
* @param {object} state Emit state.
|
|
819
|
+
/**
|
|
820
|
+
* Rejects compute shaders that need real compute-pipeline features (shared
|
|
821
|
+
* memory, barriers, atomics, raw/structured/typed UAV reads) instead of the
|
|
822
|
+
* "map-style" thread-per-fragment shape this emitter lowers. The message
|
|
823
|
+
* intentionally contains "not supported" so packaging's kill-list
|
|
824
|
+
* classifier regex (`/not supported|No GLSL lowering|unimplementable/i`)
|
|
825
|
+
* routes these to the exclusion list rather than the failure list.
|
|
826
|
+
*
|
|
827
|
+
* @param {object} state Emit state.
|
|
507
828
|
*/
|
|
508
829
|
_assertMapStyleCompute(state) {
|
|
509
830
|
for (const instruction of state.decoder.instructions) {
|
|
@@ -519,26 +840,26 @@ class DxbcGlslEmitter {
|
|
|
519
840
|
}
|
|
520
841
|
}
|
|
521
842
|
|
|
522
|
-
/**
|
|
523
|
-
* Pre-scans a map-style compute shader's `store_uav_typed` instructions and
|
|
524
|
-
* builds a per-UAV slice plan before any declaration is emitted.
|
|
525
|
-
*
|
|
526
|
-
* Straight-line constant propagation only: the address registers ASSAO-style
|
|
527
|
-
* Prepare passes build are `mov rX.<c>, l(k)` immediates, so the tracker
|
|
528
|
-
* records the last constant written to each temp component and treats every
|
|
529
|
-
* other write as dynamic. Each stored-to UAV register resolves to either
|
|
530
|
-
* `{ kind: "single" }` (at most one store; the array-slice coordinate, when
|
|
531
|
-
* present, is dropped — the host attaches the target layer via
|
|
532
|
-
* `framebufferTextureLayer`) or `{ kind: "multiSlice", slices, sliceByOffset }`
|
|
533
|
-
* (several stores at statically distinct texture2darray slices, each routed
|
|
534
|
-
* to its own fragment output — the host attaches those layers as sequential
|
|
535
|
-
* color attachments).
|
|
536
|
-
*
|
|
537
|
-
* Anything outside those two shapes cannot become one fragment pass, so the
|
|
538
|
-
* throw messages deliberately contain "not supported" for packaging's
|
|
539
|
-
* kill-list classifier (see `_assertMapStyleCompute`).
|
|
540
|
-
*
|
|
541
|
-
* @param {object} state Emit state.
|
|
843
|
+
/**
|
|
844
|
+
* Pre-scans a map-style compute shader's `store_uav_typed` instructions and
|
|
845
|
+
* builds a per-UAV slice plan before any declaration is emitted.
|
|
846
|
+
*
|
|
847
|
+
* Straight-line constant propagation only: the address registers ASSAO-style
|
|
848
|
+
* Prepare passes build are `mov rX.<c>, l(k)` immediates, so the tracker
|
|
849
|
+
* records the last constant written to each temp component and treats every
|
|
850
|
+
* other write as dynamic. Each stored-to UAV register resolves to either
|
|
851
|
+
* `{ kind: "single" }` (at most one store; the array-slice coordinate, when
|
|
852
|
+
* present, is dropped — the host attaches the target layer via
|
|
853
|
+
* `framebufferTextureLayer`) or `{ kind: "multiSlice", slices, sliceByOffset }`
|
|
854
|
+
* (several stores at statically distinct texture2darray slices, each routed
|
|
855
|
+
* to its own fragment output — the host attaches those layers as sequential
|
|
856
|
+
* color attachments).
|
|
857
|
+
*
|
|
858
|
+
* Anything outside those two shapes cannot become one fragment pass, so the
|
|
859
|
+
* throw messages deliberately contain "not supported" for packaging's
|
|
860
|
+
* kill-list classifier (see `_assertMapStyleCompute`).
|
|
861
|
+
*
|
|
862
|
+
* @param {object} state Emit state.
|
|
542
863
|
*/
|
|
543
864
|
_analyzeUavStores(state) {
|
|
544
865
|
for (const instruction of state.decoder.instructions) {
|
|
@@ -611,16 +932,16 @@ class DxbcGlslEmitter {
|
|
|
611
932
|
}
|
|
612
933
|
}
|
|
613
934
|
|
|
614
|
-
/**
|
|
615
|
-
* Updates the per-temp-component constant tracker for one instruction:
|
|
616
|
-
* `mov rX.<mask>, l(...)` records each written component's immediate dword,
|
|
617
|
-
* and any other write to a temp component invalidates it. Destination
|
|
618
|
-
* operands are exactly the mask-selection temp operands (DXBC sources
|
|
619
|
-
* always read through swizzle/select1), so this also handles multi-
|
|
620
|
-
* destination opcodes like `sincos` and `udiv` conservatively.
|
|
621
|
-
*
|
|
622
|
-
* @param {object} instruction Decoded non-declaration instruction.
|
|
623
|
-
* @param {Map<string, number>} constantTemps Constant tracker keyed `<register>.<component>`.
|
|
935
|
+
/**
|
|
936
|
+
* Updates the per-temp-component constant tracker for one instruction:
|
|
937
|
+
* `mov rX.<mask>, l(...)` records each written component's immediate dword,
|
|
938
|
+
* and any other write to a temp component invalidates it. Destination
|
|
939
|
+
* operands are exactly the mask-selection temp operands (DXBC sources
|
|
940
|
+
* always read through swizzle/select1), so this also handles multi-
|
|
941
|
+
* destination opcodes like `sincos` and `udiv` conservatively.
|
|
942
|
+
*
|
|
943
|
+
* @param {object} instruction Decoded non-declaration instruction.
|
|
944
|
+
* @param {Map<string, number>} constantTemps Constant tracker keyed `<register>.<component>`.
|
|
624
945
|
*/
|
|
625
946
|
_trackTempConstants(instruction, constantTemps) {
|
|
626
947
|
const source = instruction.operands[1];
|
|
@@ -639,13 +960,13 @@ class DxbcGlslEmitter {
|
|
|
639
960
|
}
|
|
640
961
|
}
|
|
641
962
|
|
|
642
|
-
/**
|
|
643
|
-
* Resolves the array-slice (z) component of a `store_uav_typed` address
|
|
644
|
-
* operand to a compile-time constant, or null when it is dynamic.
|
|
645
|
-
*
|
|
646
|
-
* @param {object} addressOperand Decoded address operand.
|
|
647
|
-
* @param {Map<string, number>} constantTemps Constant tracker keyed `<register>.<component>`.
|
|
648
|
-
* @returns {number|null} Constant slice index, or null.
|
|
963
|
+
/**
|
|
964
|
+
* Resolves the array-slice (z) component of a `store_uav_typed` address
|
|
965
|
+
* operand to a compile-time constant, or null when it is dynamic.
|
|
966
|
+
*
|
|
967
|
+
* @param {object} addressOperand Decoded address operand.
|
|
968
|
+
* @param {Map<string, number>} constantTemps Constant tracker keyed `<register>.<component>`.
|
|
969
|
+
* @returns {number|null} Constant slice index, or null.
|
|
649
970
|
*/
|
|
650
971
|
_resolveSliceConstant(addressOperand, constantTemps) {
|
|
651
972
|
if (addressOperand.type === 4) {
|
|
@@ -660,14 +981,14 @@ class DxbcGlslEmitter {
|
|
|
660
981
|
return constantTemps.has(key) ? constantTemps.get(key) : null;
|
|
661
982
|
}
|
|
662
983
|
|
|
663
|
-
/**
|
|
664
|
-
* Allocates the next map-style UAV fragment-output location, enforcing the
|
|
665
|
-
* `MAX_MAP_STYLE_UAV_OUTPUTS` attachment budget across single-output UAVs
|
|
666
|
-
* and per-slice multi-output UAVs alike.
|
|
667
|
-
*
|
|
668
|
-
* @param {object} state Emit state.
|
|
669
|
-
* @param {number} register UAV register index (error detail only).
|
|
670
|
-
* @returns {number} Allocated output location.
|
|
984
|
+
/**
|
|
985
|
+
* Allocates the next map-style UAV fragment-output location, enforcing the
|
|
986
|
+
* `MAX_MAP_STYLE_UAV_OUTPUTS` attachment budget across single-output UAVs
|
|
987
|
+
* and per-slice multi-output UAVs alike.
|
|
988
|
+
*
|
|
989
|
+
* @param {object} state Emit state.
|
|
990
|
+
* @param {number} register UAV register index (error detail only).
|
|
991
|
+
* @returns {number} Allocated output location.
|
|
671
992
|
*/
|
|
672
993
|
_allocateUavOutputLocation(state, register) {
|
|
673
994
|
const location = state.uavOutputCount;
|
|
@@ -686,12 +1007,12 @@ class DxbcGlslEmitter {
|
|
|
686
1007
|
// Declarations
|
|
687
1008
|
// ------------------------------------------------------------------
|
|
688
1009
|
|
|
689
|
-
/**
|
|
690
|
-
* Emits one decoded DXBC declaration into the GLSL interface and manifest.
|
|
691
|
-
*
|
|
692
|
-
* @param {object} state Mutable emission state.
|
|
693
|
-
* @param {object} instruction Decoded DXBC declaration instruction.
|
|
694
|
-
* @private
|
|
1010
|
+
/**
|
|
1011
|
+
* Emits one decoded DXBC declaration into the GLSL interface and manifest.
|
|
1012
|
+
*
|
|
1013
|
+
* @param {object} state Mutable emission state.
|
|
1014
|
+
* @param {object} instruction Decoded DXBC declaration instruction.
|
|
1015
|
+
* @private
|
|
695
1016
|
*/
|
|
696
1017
|
_emitDeclaration(state, instruction) {
|
|
697
1018
|
const declaration = instruction.declaration;
|
|
@@ -795,12 +1116,21 @@ class DxbcGlslEmitter {
|
|
|
795
1116
|
state.resourceDimensions.set(register, declaration.resourceDimension);
|
|
796
1117
|
state.resourceNames.set(register, name);
|
|
797
1118
|
state.declarationLines.push(`uniform mediump ${samplerType} ${name};`);
|
|
1119
|
+
// The sampler this texture is actually paired with at its sample
|
|
1120
|
+
// sites. A texture sampled through more than one sampler cannot
|
|
1121
|
+
// be represented by one GLSL uniform's state, so record them all
|
|
1122
|
+
// and let the packaging layer decide; a texture sampled through
|
|
1123
|
+
// none (pure `ld`) simply has no sampler state to carry.
|
|
1124
|
+
const paired = state.pairedSamplers.get(register);
|
|
798
1125
|
state.bindings.push({
|
|
799
1126
|
kind: "resource",
|
|
800
1127
|
registerIndex: register,
|
|
801
1128
|
name,
|
|
802
1129
|
samplerType,
|
|
803
1130
|
dimensionName: declaration.resourceDimensionName,
|
|
1131
|
+
...(paired?.size ? {
|
|
1132
|
+
pairedSamplerRegisters: [...paired].sort((a, b) => a - b)
|
|
1133
|
+
} : {}),
|
|
804
1134
|
...(comparisonSamplers ? {
|
|
805
1135
|
comparison: true,
|
|
806
1136
|
samplerRegisterIndices: [...comparisonSamplers].sort((a, b) => a - b)
|
|
@@ -978,24 +1308,24 @@ class DxbcGlslEmitter {
|
|
|
978
1308
|
}
|
|
979
1309
|
}
|
|
980
1310
|
|
|
981
|
-
/**
|
|
982
|
-
* Finds the signature element assigned to a register.
|
|
983
|
-
*
|
|
984
|
-
* @param {object|null} signature Decoded DXBC signature.
|
|
985
|
-
* @param {number} registerIndex Register index.
|
|
986
|
-
* @returns {object|null} Matching signature element.
|
|
987
|
-
* @private
|
|
1311
|
+
/**
|
|
1312
|
+
* Finds the signature element assigned to a register.
|
|
1313
|
+
*
|
|
1314
|
+
* @param {object|null} signature Decoded DXBC signature.
|
|
1315
|
+
* @param {number} registerIndex Register index.
|
|
1316
|
+
* @returns {object|null} Matching signature element.
|
|
1317
|
+
* @private
|
|
988
1318
|
*/
|
|
989
1319
|
_signatureElement(signature, registerIndex) {
|
|
990
1320
|
return signature?.elements.find(element => element.registerIndex === registerIndex) || null;
|
|
991
1321
|
}
|
|
992
1322
|
|
|
993
|
-
/**
|
|
994
|
-
* Declares one vertex input using its Carbon semantic identity.
|
|
995
|
-
*
|
|
996
|
-
* @param {object} state Mutable emission state.
|
|
997
|
-
* @param {object} instruction Decoded input declaration.
|
|
998
|
-
* @private
|
|
1323
|
+
/**
|
|
1324
|
+
* Declares one vertex input using its Carbon semantic identity.
|
|
1325
|
+
*
|
|
1326
|
+
* @param {object} state Mutable emission state.
|
|
1327
|
+
* @param {object} instruction Decoded input declaration.
|
|
1328
|
+
* @private
|
|
999
1329
|
*/
|
|
1000
1330
|
_declareVertexInput(state, instruction) {
|
|
1001
1331
|
const register = instruction.declaration.registerIndex;
|
|
@@ -1037,12 +1367,12 @@ class DxbcGlslEmitter {
|
|
|
1037
1367
|
});
|
|
1038
1368
|
}
|
|
1039
1369
|
|
|
1040
|
-
/**
|
|
1041
|
-
* Declares one pixel-stage varying or system input.
|
|
1042
|
-
*
|
|
1043
|
-
* @param {object} state Mutable emission state.
|
|
1044
|
-
* @param {object} instruction Decoded input declaration.
|
|
1045
|
-
* @private
|
|
1370
|
+
/**
|
|
1371
|
+
* Declares one pixel-stage varying or system input.
|
|
1372
|
+
*
|
|
1373
|
+
* @param {object} state Mutable emission state.
|
|
1374
|
+
* @param {object} instruction Decoded input declaration.
|
|
1375
|
+
* @private
|
|
1046
1376
|
*/
|
|
1047
1377
|
_declarePixelInput(state, instruction) {
|
|
1048
1378
|
const register = instruction.declaration.registerIndex;
|
|
@@ -1082,12 +1412,12 @@ class DxbcGlslEmitter {
|
|
|
1082
1412
|
});
|
|
1083
1413
|
}
|
|
1084
1414
|
|
|
1085
|
-
/**
|
|
1086
|
-
* Maps a supported system-generated input to its GLSL expression.
|
|
1087
|
-
*
|
|
1088
|
-
* @param {object} state Mutable emission state.
|
|
1089
|
-
* @param {object} instruction Decoded system-input declaration.
|
|
1090
|
-
* @private
|
|
1415
|
+
/**
|
|
1416
|
+
* Maps a supported system-generated input to its GLSL expression.
|
|
1417
|
+
*
|
|
1418
|
+
* @param {object} state Mutable emission state.
|
|
1419
|
+
* @param {object} instruction Decoded system-input declaration.
|
|
1420
|
+
* @private
|
|
1091
1421
|
*/
|
|
1092
1422
|
_declareSystemInput(state, instruction) {
|
|
1093
1423
|
const declaration = instruction.declaration;
|
|
@@ -1110,16 +1440,16 @@ class DxbcGlslEmitter {
|
|
|
1110
1440
|
}
|
|
1111
1441
|
}
|
|
1112
1442
|
|
|
1113
|
-
/**
|
|
1114
|
-
* Records which compute pseudo-input(s) a `dcl_input` declaration exposes.
|
|
1115
|
-
* The actual GLSL (uniform + derived locals) is emitted once, after every
|
|
1116
|
-
* declaration has been seen, by `_emitComputeInputPrelude` — `dcl_input`
|
|
1117
|
-
* for a thread-id kind can appear before or after `dcl_thread_group` in the
|
|
1118
|
-
* bytecode stream, and the flattened/group-id forms need the workgroup
|
|
1119
|
-
* size regardless of declaration order.
|
|
1120
|
-
*
|
|
1121
|
-
* @param {object} state Emit state.
|
|
1122
|
-
* @param {object} instruction `dcl_input` instruction with a thread-id-kind operand.
|
|
1443
|
+
/**
|
|
1444
|
+
* Records which compute pseudo-input(s) a `dcl_input` declaration exposes.
|
|
1445
|
+
* The actual GLSL (uniform + derived locals) is emitted once, after every
|
|
1446
|
+
* declaration has been seen, by `_emitComputeInputPrelude` — `dcl_input`
|
|
1447
|
+
* for a thread-id kind can appear before or after `dcl_thread_group` in the
|
|
1448
|
+
* bytecode stream, and the flattened/group-id forms need the workgroup
|
|
1449
|
+
* size regardless of declaration order.
|
|
1450
|
+
*
|
|
1451
|
+
* @param {object} state Emit state.
|
|
1452
|
+
* @param {object} instruction `dcl_input` instruction with a thread-id-kind operand.
|
|
1123
1453
|
*/
|
|
1124
1454
|
_declareComputeInput(state, instruction) {
|
|
1125
1455
|
const kind = instruction.declaration.operandTypeName;
|
|
@@ -1144,13 +1474,13 @@ class DxbcGlslEmitter {
|
|
|
1144
1474
|
}
|
|
1145
1475
|
}
|
|
1146
1476
|
|
|
1147
|
-
/**
|
|
1148
|
-
* Emits the shared `gl_FragCoord`-derived thread-id prelude for map-style
|
|
1149
|
-
* compute shaders: a `cjsDispatchOrigin` uniform (so a dispatch can be
|
|
1150
|
-
* tiled across more than one draw/viewport), then only the intermediate
|
|
1151
|
-
* and final locals this shader actually declared inputs for.
|
|
1152
|
-
*
|
|
1153
|
-
* @param {object} state Emit state.
|
|
1477
|
+
/**
|
|
1478
|
+
* Emits the shared `gl_FragCoord`-derived thread-id prelude for map-style
|
|
1479
|
+
* compute shaders: a `cjsDispatchOrigin` uniform (so a dispatch can be
|
|
1480
|
+
* tiled across more than one draw/viewport), then only the intermediate
|
|
1481
|
+
* and final locals this shader actually declared inputs for.
|
|
1482
|
+
*
|
|
1483
|
+
* @param {object} state Emit state.
|
|
1154
1484
|
*/
|
|
1155
1485
|
_emitComputeInputPrelude(state) {
|
|
1156
1486
|
const used = state.computeInputsUsed;
|
|
@@ -1185,12 +1515,12 @@ class DxbcGlslEmitter {
|
|
|
1185
1515
|
}
|
|
1186
1516
|
}
|
|
1187
1517
|
|
|
1188
|
-
/**
|
|
1189
|
-
* Reads the workgroup size recorded by `dcl_thread_group`, required to
|
|
1190
|
-
* split the flat thread id back into group id / id-in-group components.
|
|
1191
|
-
*
|
|
1192
|
-
* @param {object} state Emit state.
|
|
1193
|
-
* @returns {[number,number,number]} Workgroup size.
|
|
1518
|
+
/**
|
|
1519
|
+
* Reads the workgroup size recorded by `dcl_thread_group`, required to
|
|
1520
|
+
* split the flat thread id back into group id / id-in-group components.
|
|
1521
|
+
*
|
|
1522
|
+
* @param {object} state Emit state.
|
|
1523
|
+
* @returns {[number,number,number]} Workgroup size.
|
|
1194
1524
|
*/
|
|
1195
1525
|
_threadGroupSizeOrThrow(state) {
|
|
1196
1526
|
if (!Array.isArray(state.threadGroup)) {
|
|
@@ -1201,12 +1531,12 @@ class DxbcGlslEmitter {
|
|
|
1201
1531
|
return state.threadGroup;
|
|
1202
1532
|
}
|
|
1203
1533
|
|
|
1204
|
-
/**
|
|
1205
|
-
* Declares one stage output and records its backend identity.
|
|
1206
|
-
*
|
|
1207
|
-
* @param {object} state Mutable emission state.
|
|
1208
|
-
* @param {object} instruction Decoded output declaration.
|
|
1209
|
-
* @private
|
|
1534
|
+
/**
|
|
1535
|
+
* Declares one stage output and records its backend identity.
|
|
1536
|
+
*
|
|
1537
|
+
* @param {object} state Mutable emission state.
|
|
1538
|
+
* @param {object} instruction Decoded output declaration.
|
|
1539
|
+
* @private
|
|
1210
1540
|
*/
|
|
1211
1541
|
_declareOutput(state, instruction) {
|
|
1212
1542
|
const declaration = instruction.declaration;
|
|
@@ -1264,12 +1594,12 @@ class DxbcGlslEmitter {
|
|
|
1264
1594
|
}
|
|
1265
1595
|
}
|
|
1266
1596
|
|
|
1267
|
-
/**
|
|
1268
|
-
* Materializes an immediate constant-buffer declaration.
|
|
1269
|
-
*
|
|
1270
|
-
* @param {object} state Mutable emission state.
|
|
1271
|
-
* @param {object} instruction Decoded custom-data declaration.
|
|
1272
|
-
* @private
|
|
1597
|
+
/**
|
|
1598
|
+
* Materializes an immediate constant-buffer declaration.
|
|
1599
|
+
*
|
|
1600
|
+
* @param {object} state Mutable emission state.
|
|
1601
|
+
* @param {object} instruction Decoded custom-data declaration.
|
|
1602
|
+
* @private
|
|
1273
1603
|
*/
|
|
1274
1604
|
_declareImmediateConstantBuffer(state, instruction) {
|
|
1275
1605
|
const rows = instruction.customData?.immediateConstantBuffer;
|
|
@@ -1280,11 +1610,11 @@ class DxbcGlslEmitter {
|
|
|
1280
1610
|
state.declarationLines.push(`const vec4 ${state.icbName}[${rows.length}] = vec4[${rows.length}](`, ...literals.map((literal, index) => ` ${literal}${index < literals.length - 1 ? "," : ""}`), ");");
|
|
1281
1611
|
}
|
|
1282
1612
|
|
|
1283
|
-
/**
|
|
1284
|
-
* Initializes paired vertex outputs that the source shader does not write.
|
|
1285
|
-
*
|
|
1286
|
-
* @param {object} state Mutable emission state.
|
|
1287
|
-
* @private
|
|
1613
|
+
/**
|
|
1614
|
+
* Initializes paired vertex outputs that the source shader does not write.
|
|
1615
|
+
*
|
|
1616
|
+
* @param {object} state Mutable emission state.
|
|
1617
|
+
* @private
|
|
1288
1618
|
*/
|
|
1289
1619
|
_zeroFillUnwrittenOutputs(state) {
|
|
1290
1620
|
const written = new Set();
|
|
@@ -1309,12 +1639,12 @@ class DxbcGlslEmitter {
|
|
|
1309
1639
|
// Instructions
|
|
1310
1640
|
// ------------------------------------------------------------------
|
|
1311
1641
|
|
|
1312
|
-
/**
|
|
1313
|
-
* Dispatches one decoded instruction to its GLSL lowering rule.
|
|
1314
|
-
*
|
|
1315
|
-
* @param {object} state Mutable emission state.
|
|
1316
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1317
|
-
* @private
|
|
1642
|
+
/**
|
|
1643
|
+
* Dispatches one decoded instruction to its GLSL lowering rule.
|
|
1644
|
+
*
|
|
1645
|
+
* @param {object} state Mutable emission state.
|
|
1646
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1647
|
+
* @private
|
|
1318
1648
|
*/
|
|
1319
1649
|
_emitInstruction(state, instruction) {
|
|
1320
1650
|
const lower = DxbcGlslEmitter.LOWERINGS[instruction.opcodeName];
|
|
@@ -1328,23 +1658,23 @@ class DxbcGlslEmitter {
|
|
|
1328
1658
|
lower.call(this, state, instruction);
|
|
1329
1659
|
}
|
|
1330
1660
|
|
|
1331
|
-
/**
|
|
1332
|
-
* Appends one correctly indented line to the emitted function body.
|
|
1333
|
-
*
|
|
1334
|
-
* @param {object} state Mutable emission state.
|
|
1335
|
-
* @param {string} text GLSL source line.
|
|
1336
|
-
* @private
|
|
1661
|
+
/**
|
|
1662
|
+
* Appends one correctly indented line to the emitted function body.
|
|
1663
|
+
*
|
|
1664
|
+
* @param {object} state Mutable emission state.
|
|
1665
|
+
* @param {string} text GLSL source line.
|
|
1666
|
+
* @private
|
|
1337
1667
|
*/
|
|
1338
1668
|
_line(state, text) {
|
|
1339
1669
|
state.bodyLines.push(`${" ".repeat(state.indent)}${text}`);
|
|
1340
1670
|
}
|
|
1341
1671
|
|
|
1342
|
-
/**
|
|
1343
|
-
* Counts the active components in a DXBC mask.
|
|
1344
|
-
*
|
|
1345
|
-
* @param {number} mask DXBC component bit mask.
|
|
1346
|
-
* @returns {number} Active component count.
|
|
1347
|
-
* @private
|
|
1672
|
+
/**
|
|
1673
|
+
* Counts the active components in a DXBC mask.
|
|
1674
|
+
*
|
|
1675
|
+
* @param {number} mask DXBC component bit mask.
|
|
1676
|
+
* @returns {number} Active component count.
|
|
1677
|
+
* @private
|
|
1348
1678
|
*/
|
|
1349
1679
|
_maskWidth(mask) {
|
|
1350
1680
|
let width = 0;
|
|
@@ -1354,51 +1684,51 @@ class DxbcGlslEmitter {
|
|
|
1354
1684
|
return width || 4;
|
|
1355
1685
|
}
|
|
1356
1686
|
|
|
1357
|
-
/**
|
|
1358
|
-
* Converts a DXBC component bit mask to GLSL swizzle characters.
|
|
1359
|
-
*
|
|
1360
|
-
* @param {number} mask DXBC component bit mask.
|
|
1361
|
-
* @returns {string} GLSL component string.
|
|
1362
|
-
* @private
|
|
1687
|
+
/**
|
|
1688
|
+
* Converts a DXBC component bit mask to GLSL swizzle characters.
|
|
1689
|
+
*
|
|
1690
|
+
* @param {number} mask DXBC component bit mask.
|
|
1691
|
+
* @returns {string} GLSL component string.
|
|
1692
|
+
* @private
|
|
1363
1693
|
*/
|
|
1364
1694
|
_maskChars(mask) {
|
|
1365
1695
|
return COMPONENTS.filter((_, bit) => mask & 1 << bit).join("") || "xyzw";
|
|
1366
1696
|
}
|
|
1367
1697
|
|
|
1368
|
-
/**
|
|
1369
|
-
* Builds the lvalue text for one register-space destination component,
|
|
1370
|
-
* remapped into the target variable's declared component space.
|
|
1698
|
+
/**
|
|
1699
|
+
* Builds the lvalue text for one register-space destination component,
|
|
1700
|
+
* remapped into the target variable's declared component space.
|
|
1371
1701
|
*/
|
|
1372
1702
|
_destComponentRef(state, destOperand, target, component) {
|
|
1373
1703
|
const remapped = state.formatter.remapComponents(destOperand, component);
|
|
1374
1704
|
return `${target.ref}${remapped ? `.${remapped}` : ""}`;
|
|
1375
1705
|
}
|
|
1376
1706
|
|
|
1377
|
-
/**
|
|
1378
|
-
* Formats a resolved destination reference and optional mask.
|
|
1379
|
-
*
|
|
1380
|
-
* @param {object} target Resolved destination.
|
|
1381
|
-
* @returns {string} GLSL lvalue text.
|
|
1382
|
-
* @private
|
|
1707
|
+
/**
|
|
1708
|
+
* Formats a resolved destination reference and optional mask.
|
|
1709
|
+
*
|
|
1710
|
+
* @param {object} target Resolved destination.
|
|
1711
|
+
* @returns {string} GLSL lvalue text.
|
|
1712
|
+
* @private
|
|
1383
1713
|
*/
|
|
1384
1714
|
_destText(target) {
|
|
1385
1715
|
return `${target.ref}${target.mask ? `.${target.mask}` : ""}`;
|
|
1386
1716
|
}
|
|
1387
1717
|
|
|
1388
|
-
/**
|
|
1389
|
-
* Selects the GLSL floating scalar or vector type for a width.
|
|
1390
|
-
*
|
|
1391
|
-
* @param {number} width Component width.
|
|
1392
|
-
* @returns {string} GLSL type name.
|
|
1393
|
-
* @private
|
|
1718
|
+
/**
|
|
1719
|
+
* Selects the GLSL floating scalar or vector type for a width.
|
|
1720
|
+
*
|
|
1721
|
+
* @param {number} width Component width.
|
|
1722
|
+
* @returns {string} GLSL type name.
|
|
1723
|
+
* @private
|
|
1394
1724
|
*/
|
|
1395
1725
|
_vecType(width) {
|
|
1396
1726
|
return width === 1 ? "float" : `vec${width}`;
|
|
1397
1727
|
}
|
|
1398
1728
|
|
|
1399
|
-
/**
|
|
1400
|
-
* Reads a source at the destination mask's width, wrapping in a matching
|
|
1401
|
-
* constructor so intrinsic arguments always type-check exactly.
|
|
1729
|
+
/**
|
|
1730
|
+
* Reads a source at the destination mask's width, wrapping in a matching
|
|
1731
|
+
* constructor so intrinsic arguments always type-check exactly.
|
|
1402
1732
|
*/
|
|
1403
1733
|
_vecArg(state, operand, destMask, as = "float") {
|
|
1404
1734
|
const expression = state.formatter.sourceExpression(operand, {
|
|
@@ -1416,14 +1746,14 @@ class DxbcGlslEmitter {
|
|
|
1416
1746
|
return `${ctor}(${expression})`;
|
|
1417
1747
|
}
|
|
1418
1748
|
|
|
1419
|
-
/**
|
|
1420
|
-
* Resolves a destination operand and its register-space component mask.
|
|
1421
|
-
*
|
|
1422
|
-
* @param {object} state Mutable emission state.
|
|
1423
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1424
|
-
* @param {number} [operandIndex=0] Destination operand index.
|
|
1425
|
-
* @returns {{target: object|null, mask: string}} Destination and mask.
|
|
1426
|
-
* @private
|
|
1749
|
+
/**
|
|
1750
|
+
* Resolves a destination operand and its register-space component mask.
|
|
1751
|
+
*
|
|
1752
|
+
* @param {object} state Mutable emission state.
|
|
1753
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1754
|
+
* @param {number} [operandIndex=0] Destination operand index.
|
|
1755
|
+
* @returns {{target: object|null, mask: string}} Destination and mask.
|
|
1756
|
+
* @private
|
|
1427
1757
|
*/
|
|
1428
1758
|
_destMask(state, instruction, operandIndex = 0) {
|
|
1429
1759
|
const target = state.formatter.destination(instruction.operands[operandIndex]);
|
|
@@ -1436,25 +1766,25 @@ class DxbcGlslEmitter {
|
|
|
1436
1766
|
};
|
|
1437
1767
|
}
|
|
1438
1768
|
|
|
1439
|
-
/**
|
|
1440
|
-
* Tests whether an operand produces one scalar component.
|
|
1441
|
-
*
|
|
1442
|
-
* @param {object} operand Decoded DXBC operand.
|
|
1443
|
-
* @returns {boolean} True when the source is scalar.
|
|
1444
|
-
* @private
|
|
1769
|
+
/**
|
|
1770
|
+
* Tests whether an operand produces one scalar component.
|
|
1771
|
+
*
|
|
1772
|
+
* @param {object} operand Decoded DXBC operand.
|
|
1773
|
+
* @returns {boolean} True when the source is scalar.
|
|
1774
|
+
* @private
|
|
1445
1775
|
*/
|
|
1446
1776
|
_isScalarSource(operand) {
|
|
1447
1777
|
return operand.selectionModeName === "select1" || operand.type === 4 && operand.componentCount === 1 || operand.componentCount === 1;
|
|
1448
1778
|
}
|
|
1449
1779
|
|
|
1450
|
-
/**
|
|
1451
|
-
* Emits an assignment and applies instruction saturation when required.
|
|
1452
|
-
*
|
|
1453
|
-
* @param {object} state Mutable emission state.
|
|
1454
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1455
|
-
* @param {string} valueExpression GLSL right-hand expression.
|
|
1456
|
-
* @param {object} [options={}] Assignment formatting options.
|
|
1457
|
-
* @private
|
|
1780
|
+
/**
|
|
1781
|
+
* Emits an assignment and applies instruction saturation when required.
|
|
1782
|
+
*
|
|
1783
|
+
* @param {object} state Mutable emission state.
|
|
1784
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1785
|
+
* @param {string} valueExpression GLSL right-hand expression.
|
|
1786
|
+
* @param {object} [options={}] Assignment formatting options.
|
|
1787
|
+
* @private
|
|
1458
1788
|
*/
|
|
1459
1789
|
_assign(state, instruction, valueExpression, options = {}) {
|
|
1460
1790
|
const statement = state.formatter.assignment(instruction.operands[0], valueExpression, options);
|
|
@@ -1470,13 +1800,13 @@ class DxbcGlslEmitter {
|
|
|
1470
1800
|
}
|
|
1471
1801
|
}
|
|
1472
1802
|
|
|
1473
|
-
/**
|
|
1474
|
-
* Emits a width-correct floating binary infix operation.
|
|
1475
|
-
*
|
|
1476
|
-
* @param {object} state Mutable emission state.
|
|
1477
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1478
|
-
* @param {string} operator GLSL infix operator.
|
|
1479
|
-
* @private
|
|
1803
|
+
/**
|
|
1804
|
+
* Emits a width-correct floating binary infix operation.
|
|
1805
|
+
*
|
|
1806
|
+
* @param {object} state Mutable emission state.
|
|
1807
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1808
|
+
* @param {string} operator GLSL infix operator.
|
|
1809
|
+
* @private
|
|
1480
1810
|
*/
|
|
1481
1811
|
_infixBinary(state, instruction, operator) {
|
|
1482
1812
|
const {
|
|
@@ -1492,15 +1822,15 @@ class DxbcGlslEmitter {
|
|
|
1492
1822
|
this._assignWidened(state, instruction, mask, `${a} ${operator} ${b}`, width === 1);
|
|
1493
1823
|
}
|
|
1494
1824
|
|
|
1495
|
-
/**
|
|
1496
|
-
* Widens a scalar result when assigning it to a vector destination.
|
|
1497
|
-
*
|
|
1498
|
-
* @param {object} state Mutable emission state.
|
|
1499
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1500
|
-
* @param {string} mask Destination component mask.
|
|
1501
|
-
* @param {string} expression GLSL expression.
|
|
1502
|
-
* @param {boolean} rhsIsScalar Whether the expression is scalar.
|
|
1503
|
-
* @private
|
|
1825
|
+
/**
|
|
1826
|
+
* Widens a scalar result when assigning it to a vector destination.
|
|
1827
|
+
*
|
|
1828
|
+
* @param {object} state Mutable emission state.
|
|
1829
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1830
|
+
* @param {string} mask Destination component mask.
|
|
1831
|
+
* @param {string} expression GLSL expression.
|
|
1832
|
+
* @param {boolean} rhsIsScalar Whether the expression is scalar.
|
|
1833
|
+
* @private
|
|
1504
1834
|
*/
|
|
1505
1835
|
_assignWidened(state, instruction, mask, expression, rhsIsScalar) {
|
|
1506
1836
|
const value = rhsIsScalar && mask.length > 1 ? `vec${mask.length}(${expression})` : expression;
|
|
@@ -1509,13 +1839,13 @@ class DxbcGlslEmitter {
|
|
|
1509
1839
|
});
|
|
1510
1840
|
}
|
|
1511
1841
|
|
|
1512
|
-
/**
|
|
1513
|
-
* Emits a unary GLSL intrinsic with destination-width handling.
|
|
1514
|
-
*
|
|
1515
|
-
* @param {object} state Mutable emission state.
|
|
1516
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1517
|
-
* @param {string} glslName GLSL intrinsic name.
|
|
1518
|
-
* @private
|
|
1842
|
+
/**
|
|
1843
|
+
* Emits a unary GLSL intrinsic with destination-width handling.
|
|
1844
|
+
*
|
|
1845
|
+
* @param {object} state Mutable emission state.
|
|
1846
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1847
|
+
* @param {string} glslName GLSL intrinsic name.
|
|
1848
|
+
* @private
|
|
1519
1849
|
*/
|
|
1520
1850
|
_helperUnary(state, instruction, glslName) {
|
|
1521
1851
|
const {
|
|
@@ -1527,13 +1857,13 @@ class DxbcGlslEmitter {
|
|
|
1527
1857
|
this._assignWidened(state, instruction, mask, `${glslName}(${src})`, state.formatter.expressionWidth(instruction.operands[1], mask) === 1);
|
|
1528
1858
|
}
|
|
1529
1859
|
|
|
1530
|
-
/**
|
|
1531
|
-
* Emits a binary GLSL intrinsic with width-matched arguments.
|
|
1532
|
-
*
|
|
1533
|
-
* @param {object} state Mutable emission state.
|
|
1534
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1535
|
-
* @param {string} glslName GLSL intrinsic name.
|
|
1536
|
-
* @private
|
|
1860
|
+
/**
|
|
1861
|
+
* Emits a binary GLSL intrinsic with width-matched arguments.
|
|
1862
|
+
*
|
|
1863
|
+
* @param {object} state Mutable emission state.
|
|
1864
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1865
|
+
* @param {string} glslName GLSL intrinsic name.
|
|
1866
|
+
* @private
|
|
1537
1867
|
*/
|
|
1538
1868
|
_helperBinary(state, instruction, glslName) {
|
|
1539
1869
|
const {
|
|
@@ -1546,13 +1876,13 @@ class DxbcGlslEmitter {
|
|
|
1546
1876
|
});
|
|
1547
1877
|
}
|
|
1548
1878
|
|
|
1549
|
-
/**
|
|
1550
|
-
* Emits a dot product and replicates its scalar result when necessary.
|
|
1551
|
-
*
|
|
1552
|
-
* @param {object} state Mutable emission state.
|
|
1553
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1554
|
-
* @param {string} sourceMask Source component mask.
|
|
1555
|
-
* @private
|
|
1879
|
+
/**
|
|
1880
|
+
* Emits a dot product and replicates its scalar result when necessary.
|
|
1881
|
+
*
|
|
1882
|
+
* @param {object} state Mutable emission state.
|
|
1883
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1884
|
+
* @param {string} sourceMask Source component mask.
|
|
1885
|
+
* @private
|
|
1556
1886
|
*/
|
|
1557
1887
|
_dotProduct(state, instruction, sourceMask) {
|
|
1558
1888
|
const {
|
|
@@ -1566,16 +1896,16 @@ class DxbcGlslEmitter {
|
|
|
1566
1896
|
});
|
|
1567
1897
|
}
|
|
1568
1898
|
|
|
1569
|
-
/**
|
|
1570
|
-
* Emits scalar or vector comparison bits in the float register model.
|
|
1571
|
-
*
|
|
1572
|
-
* @param {object} state Mutable emission state.
|
|
1573
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1574
|
-
* @param {object} options Comparison options.
|
|
1575
|
-
* @param {string} options.intrinsic GLSL vector comparison intrinsic.
|
|
1576
|
-
* @param {string} options.operator GLSL scalar comparison operator.
|
|
1577
|
-
* @param {"float"|"int"|"uint"} options.as Operand interpretation.
|
|
1578
|
-
* @private
|
|
1899
|
+
/**
|
|
1900
|
+
* Emits scalar or vector comparison bits in the float register model.
|
|
1901
|
+
*
|
|
1902
|
+
* @param {object} state Mutable emission state.
|
|
1903
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1904
|
+
* @param {object} options Comparison options.
|
|
1905
|
+
* @param {string} options.intrinsic GLSL vector comparison intrinsic.
|
|
1906
|
+
* @param {string} options.operator GLSL scalar comparison operator.
|
|
1907
|
+
* @param {"float"|"int"|"uint"} options.as Operand interpretation.
|
|
1908
|
+
* @private
|
|
1579
1909
|
*/
|
|
1580
1910
|
_comparison(state, instruction, {
|
|
1581
1911
|
intrinsic,
|
|
@@ -1602,13 +1932,13 @@ class DxbcGlslEmitter {
|
|
|
1602
1932
|
this._assign(state, instruction, `uintBitsToFloat(uvec${mask.length}(${intrinsic}(${a}, ${b})) * 0xFFFFFFFFu)`);
|
|
1603
1933
|
}
|
|
1604
1934
|
|
|
1605
|
-
/**
|
|
1606
|
-
* Emits an unsigned bitwise operation through register bitcasts.
|
|
1607
|
-
*
|
|
1608
|
-
* @param {object} state Mutable emission state.
|
|
1609
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1610
|
-
* @param {string} operator GLSL bitwise operator.
|
|
1611
|
-
* @private
|
|
1935
|
+
/**
|
|
1936
|
+
* Emits an unsigned bitwise operation through register bitcasts.
|
|
1937
|
+
*
|
|
1938
|
+
* @param {object} state Mutable emission state.
|
|
1939
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1940
|
+
* @param {string} operator GLSL bitwise operator.
|
|
1941
|
+
* @private
|
|
1612
1942
|
*/
|
|
1613
1943
|
_bitwiseBinary(state, instruction, operator) {
|
|
1614
1944
|
const {
|
|
@@ -1619,14 +1949,14 @@ class DxbcGlslEmitter {
|
|
|
1619
1949
|
this._assign(state, instruction, `uintBitsToFloat(${a} ${operator} ${b})`);
|
|
1620
1950
|
}
|
|
1621
1951
|
|
|
1622
|
-
/**
|
|
1623
|
-
* Emits a signed or unsigned integer binary operation through bitcasts.
|
|
1624
|
-
*
|
|
1625
|
-
* @param {object} state Mutable emission state.
|
|
1626
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1627
|
-
* @param {string} operator GLSL integer operator.
|
|
1628
|
-
* @param {"int"|"uint"} [as="int"] Integer interpretation.
|
|
1629
|
-
* @private
|
|
1952
|
+
/**
|
|
1953
|
+
* Emits a signed or unsigned integer binary operation through bitcasts.
|
|
1954
|
+
*
|
|
1955
|
+
* @param {object} state Mutable emission state.
|
|
1956
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1957
|
+
* @param {string} operator GLSL integer operator.
|
|
1958
|
+
* @param {"int"|"uint"} [as="int"] Integer interpretation.
|
|
1959
|
+
* @private
|
|
1630
1960
|
*/
|
|
1631
1961
|
_intBinary(state, instruction, operator, as = "int") {
|
|
1632
1962
|
const {
|
|
@@ -1638,13 +1968,13 @@ class DxbcGlslEmitter {
|
|
|
1638
1968
|
this._assign(state, instruction, `${wrap}(${a} ${operator} ${b})`);
|
|
1639
1969
|
}
|
|
1640
1970
|
|
|
1641
|
-
/**
|
|
1642
|
-
* Formats the DXBC zero or nonzero control-flow condition.
|
|
1643
|
-
*
|
|
1644
|
-
* @param {object} state Mutable emission state.
|
|
1645
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1646
|
-
* @returns {string} GLSL boolean expression.
|
|
1647
|
-
* @private
|
|
1971
|
+
/**
|
|
1972
|
+
* Formats the DXBC zero or nonzero control-flow condition.
|
|
1973
|
+
*
|
|
1974
|
+
* @param {object} state Mutable emission state.
|
|
1975
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1976
|
+
* @returns {string} GLSL boolean expression.
|
|
1977
|
+
* @private
|
|
1648
1978
|
*/
|
|
1649
1979
|
_condition(state, instruction) {
|
|
1650
1980
|
const scalar = state.formatter.sourceExpression(instruction.operands[0], {
|
|
@@ -1654,12 +1984,12 @@ class DxbcGlslEmitter {
|
|
|
1654
1984
|
return `floatBitsToUint(${scalar}) ${test} 0u`;
|
|
1655
1985
|
}
|
|
1656
1986
|
|
|
1657
|
-
/**
|
|
1658
|
-
* Emits component-wise conditional moves without aliasing destinations.
|
|
1659
|
-
*
|
|
1660
|
-
* @param {object} state Mutable emission state.
|
|
1661
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1662
|
-
* @private
|
|
1987
|
+
/**
|
|
1988
|
+
* Emits component-wise conditional moves without aliasing destinations.
|
|
1989
|
+
*
|
|
1990
|
+
* @param {object} state Mutable emission state.
|
|
1991
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
1992
|
+
* @private
|
|
1663
1993
|
*/
|
|
1664
1994
|
_movc(state, instruction) {
|
|
1665
1995
|
const destOperand = instruction.operands[0];
|
|
@@ -1699,12 +2029,12 @@ class DxbcGlslEmitter {
|
|
|
1699
2029
|
}
|
|
1700
2030
|
}
|
|
1701
2031
|
|
|
1702
|
-
/**
|
|
1703
|
-
* Emits the paired sine and cosine destinations in alias-safe order.
|
|
1704
|
-
*
|
|
1705
|
-
* @param {object} state Mutable emission state.
|
|
1706
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1707
|
-
* @private
|
|
2032
|
+
/**
|
|
2033
|
+
* Emits the paired sine and cosine destinations in alias-safe order.
|
|
2034
|
+
*
|
|
2035
|
+
* @param {object} state Mutable emission state.
|
|
2036
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
2037
|
+
* @private
|
|
1708
2038
|
*/
|
|
1709
2039
|
_sincos(state, instruction) {
|
|
1710
2040
|
const [sinDest, cosDest, angle] = instruction.operands;
|
|
@@ -1732,20 +2062,20 @@ class DxbcGlslEmitter {
|
|
|
1732
2062
|
}
|
|
1733
2063
|
}
|
|
1734
2064
|
|
|
1735
|
-
/**
|
|
1736
|
-
* Declares the merged detail-map array, once, on the first member seen.
|
|
1737
|
-
*
|
|
1738
|
-
* Each member keeps its own entry in `resourceNames` pointing at the shared
|
|
1739
|
-
* array, so every existing reference site resolves to it without knowing a
|
|
1740
|
-
* merge happened. The declared dimension deliberately stays 2D: the layer is
|
|
1741
|
-
* appended to the coordinate at each sample site rather than read out of the
|
|
1742
|
-
* shader's own operand, which has no third component to give.
|
|
1743
|
-
*
|
|
1744
|
-
* @param {object} state Mutable emission state.
|
|
1745
|
-
* @param {number} register Resource register being declared.
|
|
1746
|
-
* @param {object} declaration Decoded resource declaration.
|
|
1747
|
-
* @param {Set<number>|null} comparisonSamplers Comparison samplers, when any.
|
|
1748
|
-
* @private
|
|
2065
|
+
/**
|
|
2066
|
+
* Declares the merged detail-map array, once, on the first member seen.
|
|
2067
|
+
*
|
|
2068
|
+
* Each member keeps its own entry in `resourceNames` pointing at the shared
|
|
2069
|
+
* array, so every existing reference site resolves to it without knowing a
|
|
2070
|
+
* merge happened. The declared dimension deliberately stays 2D: the layer is
|
|
2071
|
+
* appended to the coordinate at each sample site rather than read out of the
|
|
2072
|
+
* shader's own operand, which has no third component to give.
|
|
2073
|
+
*
|
|
2074
|
+
* @param {object} state Mutable emission state.
|
|
2075
|
+
* @param {number} register Resource register being declared.
|
|
2076
|
+
* @param {object} declaration Decoded resource declaration.
|
|
2077
|
+
* @param {Set<number>|null} comparisonSamplers Comparison samplers, when any.
|
|
2078
|
+
* @private
|
|
1749
2079
|
*/
|
|
1750
2080
|
_declareDetailArrayMap(state, register, declaration, comparisonSamplers) {
|
|
1751
2081
|
// A comparison-sampled or non-2D detail map is not the family the
|
|
@@ -1761,6 +2091,15 @@ class DxbcGlslEmitter {
|
|
|
1761
2091
|
state.resourceNames.set(register, DETAIL_MAP_ARRAY_SYMBOL);
|
|
1762
2092
|
if (state.detailMapArrayDeclared) return;
|
|
1763
2093
|
state.detailMapArrayDeclared = true;
|
|
2094
|
+
|
|
2095
|
+
// One GL uniform now stands for every merged layer, so it can carry only
|
|
2096
|
+
// one sampler state. Union the layers' pairings: if they disagree the
|
|
2097
|
+
// merge itself was invalid, and the packaging layer is where that is
|
|
2098
|
+
// detectable, because only it holds the sampler values to compare.
|
|
2099
|
+
const merged = new Set();
|
|
2100
|
+
for (const layerRegister of state.detailMapArrayLayers.keys()) {
|
|
2101
|
+
for (const sampler of state.pairedSamplers.get(layerRegister) ?? []) merged.add(sampler);
|
|
2102
|
+
}
|
|
1764
2103
|
state.declarationLines.push(`uniform mediump sampler2DArray ${DETAIL_MAP_ARRAY_SYMBOL};`);
|
|
1765
2104
|
state.bindings.push({
|
|
1766
2105
|
kind: "resource",
|
|
@@ -1769,35 +2108,38 @@ class DxbcGlslEmitter {
|
|
|
1769
2108
|
samplerType: "sampler2DArray",
|
|
1770
2109
|
dimensionName: "texture2darray",
|
|
1771
2110
|
arrayLayerCount: state.detailMapArrayLayers.size,
|
|
1772
|
-
mergedFrom: [...state.detailMapArrayLayers.keys()]
|
|
2111
|
+
mergedFrom: [...state.detailMapArrayLayers.keys()],
|
|
2112
|
+
...(merged.size ? {
|
|
2113
|
+
pairedSamplerRegisters: [...merged].sort((a, b) => a - b)
|
|
2114
|
+
} : {})
|
|
1773
2115
|
});
|
|
1774
2116
|
}
|
|
1775
2117
|
|
|
1776
|
-
/**
|
|
1777
|
-
* Returns the array layer a texture operand maps to, when it was merged.
|
|
1778
|
-
*
|
|
1779
|
-
* @param {object} state Mutable emission state.
|
|
1780
|
-
* @param {object} texOperand Texture resource operand.
|
|
1781
|
-
* @returns {number|null} Layer index, or null when the register was not merged.
|
|
1782
|
-
* @private
|
|
2118
|
+
/**
|
|
2119
|
+
* Returns the array layer a texture operand maps to, when it was merged.
|
|
2120
|
+
*
|
|
2121
|
+
* @param {object} state Mutable emission state.
|
|
2122
|
+
* @param {object} texOperand Texture resource operand.
|
|
2123
|
+
* @returns {number|null} Layer index, or null when the register was not merged.
|
|
2124
|
+
* @private
|
|
1783
2125
|
*/
|
|
1784
2126
|
_detailMapArrayLayer(state, texOperand) {
|
|
1785
2127
|
const layer = state.detailMapArrayLayers.get(texOperand.registerIndex);
|
|
1786
2128
|
return layer === undefined ? null : layer;
|
|
1787
2129
|
}
|
|
1788
2130
|
|
|
1789
|
-
/**
|
|
1790
|
-
* Refuses an operation that cannot be redirected at an array layer.
|
|
1791
|
-
*
|
|
1792
|
-
* Recognising the detail family proves the resources are mergeable; it does
|
|
1793
|
-
* not prove every *use* is. A texel fetch or a size query against a merged
|
|
1794
|
-
* register would silently mean something different once the register became
|
|
1795
|
-
* one layer of an array, so those fail the build instead.
|
|
1796
|
-
*
|
|
1797
|
-
* @param {object} state Mutable emission state.
|
|
1798
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1799
|
-
* @param {object} texOperand Texture resource operand.
|
|
1800
|
-
* @private
|
|
2131
|
+
/**
|
|
2132
|
+
* Refuses an operation that cannot be redirected at an array layer.
|
|
2133
|
+
*
|
|
2134
|
+
* Recognising the detail family proves the resources are mergeable; it does
|
|
2135
|
+
* not prove every *use* is. A texel fetch or a size query against a merged
|
|
2136
|
+
* register would silently mean something different once the register became
|
|
2137
|
+
* one layer of an array, so those fail the build instead.
|
|
2138
|
+
*
|
|
2139
|
+
* @param {object} state Mutable emission state.
|
|
2140
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
2141
|
+
* @param {object} texOperand Texture resource operand.
|
|
2142
|
+
* @private
|
|
1801
2143
|
*/
|
|
1802
2144
|
_rejectDetailArrayMapUse(state, instruction, texOperand) {
|
|
1803
2145
|
if (!state.detailMapArrayLayers.has(texOperand.registerIndex)) return;
|
|
@@ -1808,14 +2150,14 @@ class DxbcGlslEmitter {
|
|
|
1808
2150
|
});
|
|
1809
2151
|
}
|
|
1810
2152
|
|
|
1811
|
-
/**
|
|
1812
|
-
* Resolves and validates the declared dimension of a texture operand.
|
|
1813
|
-
*
|
|
1814
|
-
* @param {object} state Mutable emission state.
|
|
1815
|
-
* @param {object} instruction Decoded DXBC instruction.
|
|
1816
|
-
* @param {object} texOperand Texture resource operand.
|
|
1817
|
-
* @returns {number} DXBC resource-dimension code.
|
|
1818
|
-
* @private
|
|
2153
|
+
/**
|
|
2154
|
+
* Resolves and validates the declared dimension of a texture operand.
|
|
2155
|
+
*
|
|
2156
|
+
* @param {object} state Mutable emission state.
|
|
2157
|
+
* @param {object} instruction Decoded DXBC instruction.
|
|
2158
|
+
* @param {object} texOperand Texture resource operand.
|
|
2159
|
+
* @returns {number} DXBC resource-dimension code.
|
|
2160
|
+
* @private
|
|
1819
2161
|
*/
|
|
1820
2162
|
_resourceDimension(state, instruction, texOperand) {
|
|
1821
2163
|
const dimension = state.resourceDimensions.get(texOperand.registerIndex);
|
|
@@ -1829,18 +2171,18 @@ class DxbcGlslEmitter {
|
|
|
1829
2171
|
return dimension;
|
|
1830
2172
|
}
|
|
1831
2173
|
|
|
1832
|
-
/**
|
|
1833
|
-
* Emits a regular, LOD, bias, gradient, or comparison texture sample.
|
|
1834
|
-
*
|
|
1835
|
-
* @param {object} state Mutable emission state.
|
|
1836
|
-
* @param {object} instruction Decoded DXBC texture instruction.
|
|
1837
|
-
* @param {object} [options={}] Sampling-mode operand indexes.
|
|
1838
|
-
* @param {number|null} [options.lodOperandIndex=null] Explicit LOD operand.
|
|
1839
|
-
* @param {number|null} [options.biasOperandIndex=null] LOD bias operand.
|
|
1840
|
-
* @param {number[]|null} [options.gradOperandIndexes=null] Gradient operands.
|
|
1841
|
-
* @param {number|null} [options.comparisonRefOperandIndex=null] Comparison reference operand.
|
|
1842
|
-
* @param {boolean} [options.forceLodZero=false] Whether to force the LOD-zero adaptation.
|
|
1843
|
-
* @private
|
|
2174
|
+
/**
|
|
2175
|
+
* Emits a regular, LOD, bias, gradient, or comparison texture sample.
|
|
2176
|
+
*
|
|
2177
|
+
* @param {object} state Mutable emission state.
|
|
2178
|
+
* @param {object} instruction Decoded DXBC texture instruction.
|
|
2179
|
+
* @param {object} [options={}] Sampling-mode operand indexes.
|
|
2180
|
+
* @param {number|null} [options.lodOperandIndex=null] Explicit LOD operand.
|
|
2181
|
+
* @param {number|null} [options.biasOperandIndex=null] LOD bias operand.
|
|
2182
|
+
* @param {number[]|null} [options.gradOperandIndexes=null] Gradient operands.
|
|
2183
|
+
* @param {number|null} [options.comparisonRefOperandIndex=null] Comparison reference operand.
|
|
2184
|
+
* @param {boolean} [options.forceLodZero=false] Whether to force the LOD-zero adaptation.
|
|
2185
|
+
* @private
|
|
1844
2186
|
*/
|
|
1845
2187
|
_textureSample(state, instruction, {
|
|
1846
2188
|
lodOperandIndex = null,
|
|
@@ -1948,17 +2290,18 @@ class DxbcGlslEmitter {
|
|
|
1948
2290
|
} else {
|
|
1949
2291
|
call = `texture(${texName}, ${coord})`;
|
|
1950
2292
|
}
|
|
2293
|
+
call = this._applyEmulatedAddressing(state, instruction, call, coord, coordMask);
|
|
1951
2294
|
this._assign(state, instruction, `${call}.${returnSwizzle}`, {
|
|
1952
2295
|
saturate: instruction.saturate
|
|
1953
2296
|
});
|
|
1954
2297
|
}
|
|
1955
2298
|
|
|
1956
|
-
/**
|
|
1957
|
-
* Emits an integer-coordinate texel fetch for a declared resource.
|
|
1958
|
-
*
|
|
1959
|
-
* @param {object} state Mutable emission state.
|
|
1960
|
-
* @param {object} instruction Decoded DXBC load instruction.
|
|
1961
|
-
* @private
|
|
2299
|
+
/**
|
|
2300
|
+
* Emits an integer-coordinate texel fetch for a declared resource.
|
|
2301
|
+
*
|
|
2302
|
+
* @param {object} state Mutable emission state.
|
|
2303
|
+
* @param {object} instruction Decoded DXBC load instruction.
|
|
2304
|
+
* @private
|
|
1962
2305
|
*/
|
|
1963
2306
|
_texelFetch(state, instruction) {
|
|
1964
2307
|
const {
|
|
@@ -1994,9 +2337,9 @@ class DxbcGlslEmitter {
|
|
|
1994
2337
|
}
|
|
1995
2338
|
}
|
|
1996
2339
|
|
|
1997
|
-
/**
|
|
1998
|
-
* Per-opcode lowering table. Every rule cites its family spec in
|
|
1999
|
-
* `docs/dxbc-lowering/`; templates are HLSLcc-derived, see each doc.
|
|
2340
|
+
/**
|
|
2341
|
+
* Per-opcode lowering table. Every rule cites its family spec in
|
|
2342
|
+
* `docs/dxbc-lowering/`; templates are HLSLcc-derived, see each doc.
|
|
2000
2343
|
*/
|
|
2001
2344
|
DxbcGlslEmitter.LOWERINGS = {
|
|
2002
2345
|
// --- float-alu ---
|
|
@@ -2571,11 +2914,11 @@ DxbcGlslEmitter.LOWERINGS = {
|
|
|
2571
2914
|
}
|
|
2572
2915
|
};
|
|
2573
2916
|
|
|
2574
|
-
/**
|
|
2575
|
-
* `ld_structured` lowering. Vertex UBO path reads `name.data[row]` vec4 rows;
|
|
2576
|
-
* pixel data-texture path reads RGBA32UI texels (4 dwords per texel, fixed
|
|
2577
|
-
* 2048-texel row width). 16-byte-aligned reads use one static channel per
|
|
2578
|
-
* component; unaligned reads fall back to per-dword dynamic addressing.
|
|
2917
|
+
/**
|
|
2918
|
+
* `ld_structured` lowering. Vertex UBO path reads `name.data[row]` vec4 rows;
|
|
2919
|
+
* pixel data-texture path reads RGBA32UI texels (4 dwords per texel, fixed
|
|
2920
|
+
* 2048-texel row width). 16-byte-aligned reads use one static channel per
|
|
2921
|
+
* component; unaligned reads fall back to per-dword dynamic addressing.
|
|
2579
2922
|
*/
|
|
2580
2923
|
DxbcGlslEmitter.prototype._ldStructured = function _ldStructured(state, instruction) {
|
|
2581
2924
|
const target = state.formatter.destination(instruction.operands[0]);
|
|
@@ -2654,10 +2997,10 @@ DxbcGlslEmitter.prototype._ldStructured = function _ldStructured(state, instruct
|
|
|
2654
2997
|
}
|
|
2655
2998
|
};
|
|
2656
2999
|
|
|
2657
|
-
/**
|
|
2658
|
-
* `resinfo` lowering: width/height/depth via textureSize per live destination
|
|
2659
|
-
* component; the 4th (mip-count) channel has no WebGL2 equivalent and throws
|
|
2660
|
-
* only when a shader actually reads it.
|
|
3000
|
+
/**
|
|
3001
|
+
* `resinfo` lowering: width/height/depth via textureSize per live destination
|
|
3002
|
+
* component; the 4th (mip-count) channel has no WebGL2 equivalent and throws
|
|
3003
|
+
* only when a shader actually reads it.
|
|
2661
3004
|
*/
|
|
2662
3005
|
DxbcGlslEmitter.prototype._resinfo = function _resinfo(state, instruction) {
|
|
2663
3006
|
const {
|
|
@@ -2701,8 +3044,8 @@ DxbcGlslEmitter.prototype._resinfo = function _resinfo(state, instruction) {
|
|
|
2701
3044
|
}
|
|
2702
3045
|
};
|
|
2703
3046
|
|
|
2704
|
-
/**
|
|
2705
|
-
* Shared int/uint min-max lowering used by imax/imin/umax/umin.
|
|
3047
|
+
/**
|
|
3048
|
+
* Shared int/uint min-max lowering used by imax/imin/umax/umin.
|
|
2706
3049
|
*/
|
|
2707
3050
|
DxbcGlslEmitter.prototype._intMinMax = function _intMinMax(state, instruction, fn, as) {
|
|
2708
3051
|
const {
|
|
@@ -2714,16 +3057,16 @@ DxbcGlslEmitter.prototype._intMinMax = function _intMinMax(state, instruction, f
|
|
|
2714
3057
|
this._assign(state, instruction, `${wrap}(${fn}(${a}, ${b}))`);
|
|
2715
3058
|
};
|
|
2716
3059
|
|
|
2717
|
-
/**
|
|
2718
|
-
* `gather4` lowering: fetches one channel from each of the four texels a
|
|
2719
|
-
* bilinear sample would blend, via the `hlslcc_textureGather4Emulated` /
|
|
2720
|
-
* `hlslcc_textureGather4ArrayEmulated` helpers (GLSL ES 3.00 has no
|
|
2721
|
-
* `textureGather`). Only 2D and 2D-array resources are supported — the
|
|
2722
|
-
* dimensions the AO/blur/FSR/CAS map-style corpus this lowering targets is
|
|
2723
|
-
* known to use (ASSAO gathers from its deinterleaved texture2darray slices).
|
|
2724
|
-
*
|
|
2725
|
-
* @param {object} state Emit state.
|
|
2726
|
-
* @param {object} instruction `gather4` instruction.
|
|
3060
|
+
/**
|
|
3061
|
+
* `gather4` lowering: fetches one channel from each of the four texels a
|
|
3062
|
+
* bilinear sample would blend, via the `hlslcc_textureGather4Emulated` /
|
|
3063
|
+
* `hlslcc_textureGather4ArrayEmulated` helpers (GLSL ES 3.00 has no
|
|
3064
|
+
* `textureGather`). Only 2D and 2D-array resources are supported — the
|
|
3065
|
+
* dimensions the AO/blur/FSR/CAS map-style corpus this lowering targets is
|
|
3066
|
+
* known to use (ASSAO gathers from its deinterleaved texture2darray slices).
|
|
3067
|
+
*
|
|
3068
|
+
* @param {object} state Emit state.
|
|
3069
|
+
* @param {object} instruction `gather4` instruction.
|
|
2727
3070
|
*/
|
|
2728
3071
|
DxbcGlslEmitter.prototype._gather4 = function _gather4(state, instruction) {
|
|
2729
3072
|
const {
|
|
@@ -2757,13 +3100,13 @@ DxbcGlslEmitter.prototype._gather4 = function _gather4(state, instruction) {
|
|
|
2757
3100
|
});
|
|
2758
3101
|
};
|
|
2759
3102
|
|
|
2760
|
-
/**
|
|
2761
|
-
* Reads the gathered-channel selector `gather4`/`gather4_po` carry on their
|
|
2762
|
-
* sampler operand (a 1-component swizzle; red/`x` is the default when no
|
|
2763
|
-
* selection is encoded).
|
|
2764
|
-
*
|
|
2765
|
-
* @param {object} operand Decoded sampler operand.
|
|
2766
|
-
* @returns {string} Channel letter (`x`/`y`/`z`/`w`).
|
|
3103
|
+
/**
|
|
3104
|
+
* Reads the gathered-channel selector `gather4`/`gather4_po` carry on their
|
|
3105
|
+
* sampler operand (a 1-component swizzle; red/`x` is the default when no
|
|
3106
|
+
* selection is encoded).
|
|
3107
|
+
*
|
|
3108
|
+
* @param {object} operand Decoded sampler operand.
|
|
3109
|
+
* @returns {string} Channel letter (`x`/`y`/`z`/`w`).
|
|
2767
3110
|
*/
|
|
2768
3111
|
DxbcGlslEmitter.prototype._gather4Channel = function _gather4Channel(operand) {
|
|
2769
3112
|
if (operand.selectionModeName === "select1" && operand.selected) {
|
|
@@ -2775,8 +3118,8 @@ DxbcGlslEmitter.prototype._gather4Channel = function _gather4Channel(operand) {
|
|
|
2775
3118
|
return "x";
|
|
2776
3119
|
};
|
|
2777
3120
|
|
|
2778
|
-
/**
|
|
2779
|
-
* Assembles the final GLSL text.
|
|
3121
|
+
/**
|
|
3122
|
+
* Assembles the final GLSL text.
|
|
2780
3123
|
*/
|
|
2781
3124
|
DxbcGlslEmitter.prototype._assemble = function _assemble(state) {
|
|
2782
3125
|
const lines = ["#version 300 es"];
|